harness_space/complexes/mod.rs
1//! # Complexes Module
2//!
3//! This module provides data structures and algorithms for working with various types of
4//! topological complexes, which are fundamental tools in algebraic topology for representing
5//! and analyzing the structure of topological spaces.
6//!
7//! ## Mathematical Background
8//!
9//! A **cell complex** (or CW complex) is a topological space constructed by gluing together
10//! cells of various dimensions. The key insight is that complex topological spaces can be
11//! built up systematically from simple pieces:
12//!
13//! - **0-cells**: Points (vertices)
14//! - **1-cells**: Edges connecting vertices
15//! - **2-cells**: Faces (triangles, squares) with edges as boundaries
16//! - **k-cells**: Higher-dimensional analogs
17//!
18//! This module focuses on two important special cases:
19//! - **Simplicial complexes**: Built from simplices (points, edges, triangles, tetrahedra, ...)
20//! - **Cubical complexes**: Built from cubes (points, edges, squares, cubes, ...)
21//!
22//! ## Core Abstractions
23//!
24//! ### Generic Complex Structure
25//!
26//! The [`Complex<T>`] type provides a generic container that can work with any element type
27//! implementing [`ComplexElement`]. This allows the same algorithms (homology computation,
28//! boundary operators, etc.) to work on both simplicial and cubical complexes.
29//!
30//! ### Face Relations and Closure Property
31//!
32//! All complexes satisfy the **closure property**: if a k-cell is in the complex, then all
33//! of its faces (boundary cells) must also be in the complex. This is automatically
34//! enforced when adding elements via [`Complex::join_element`].
35//!
36//! ### Orientation and Boundary Operators
37//!
38//! Each element type implements its own boundary operator via
39//! [`ComplexElement::boundary_with_orientations`], which returns faces with their correct
40//! orientation coefficients. This enables:
41//! - Computation of boundary matrices for homology
42//! - Chain complex structure (∂² = 0)
43//! - Proper handling of orientation in both simplicial and cubical settings
44//!
45//! ## Usage Patterns
46//!
47//! ### Basic Complex Construction
48//!
49//! ```rust
50//! use harness_space::complexes::{Complex, Simplex};
51//!
52//! let mut complex = Complex::new();
53//!
54//! // Add a triangle - automatically includes all faces
55//! let triangle = Simplex::new(2, vec![0, 1, 2]);
56//! let added_triangle = complex.join_element(triangle);
57//!
58//! // Complex now contains: 1 triangle, 3 edges, 3 vertices
59//! assert_eq!(complex.elements_of_dimension(2).len(), 1);
60//! assert_eq!(complex.elements_of_dimension(1).len(), 3);
61//! assert_eq!(complex.elements_of_dimension(0).len(), 3);
62//! ```
63//!
64//! ### Homology Computation
65//!
66//! ```rust
67//! use harness_algebra::algebras::boolean::Boolean;
68//! use harness_space::{
69//! complexes::{Simplex, SimplicialComplex},
70//! prelude::*,
71//! };
72//!
73//! let mut complex = SimplicialComplex::new();
74//! let triangle = Simplex::new(2, vec![0, 1, 2]);
75//!
76//! // Compute homology over Z/2Z
77//! let h0 = complex.homology::<Boolean>(0); // Connected components
78//! let h1 = complex.homology::<Boolean>(1); // 1D holes
79//!
80//! println!("β₀ = {}, β₁ = {}", h0.betti_number, h1.betti_number);
81//! ```
82//!
83//! ### Working with Different Element Types
84//!
85//! ```rust
86//! use harness_space::complexes::{Cube, CubicalComplex, Simplex, SimplicialComplex};
87//!
88//! // Simplicial complex with triangles
89//! let mut simplicial = SimplicialComplex::new();
90//! let triangle = Simplex::new(2, vec![0, 1, 2]);
91//! simplicial.join_element(triangle);
92//!
93//! // Cubical complex with squares
94//! let mut cubical = CubicalComplex::new();
95//! let square = Cube::square([0, 1, 2, 3]);
96//! cubical.join_element(square);
97//!
98//! // Both support the same operations
99//! assert_eq!(simplicial.max_dimension(), 2);
100//! assert_eq!(cubical.max_dimension(), 2);
101//! ```
102//!
103//! ## Implementation Details
104//!
105//! ### Efficient Storage and Lookup
106//!
107//! - Elements are stored in a `HashMap<usize, T>` keyed by unique IDs
108//! - Face relationships are tracked in a `Lattice<usize>` using IDs for efficiency
109//! - Duplicate elements (same mathematical content) are automatically deduplicated
110//! - ID assignment is automatic but can be controlled when needed
111//!
112//! ### Poset Structure
113//!
114//! Complexes implement [`Poset`] where the partial order represents the face relation:
115//! `a ≤ b` means "a is a face of b". This enables:
116//! - Efficient computation of upsets/downsets (star/closure operations)
117//! - Join/meet operations for common faces/cofaces
118//! - Integration with other algebraic structures
119//!
120//! ### Topology Interface
121//!
122//! Complexes implement [`Topology`] providing:
123//! - Neighborhood operations (finding cofaces)
124//! - Boundary operators returning [`Chain`] objects
125//! - Integration with homology and sheaf computations
126//!
127//! ## Submodules
128//!
129//! - [`simplicial`]: Definitions for [`Simplex`] and simplicial complex operations
130//! - [`cubical`]: Definitions for [`Cube`] and cubical complex operations
131//!
132//! ## Examples
133//!
134//! See the extensive test suite at the bottom of this module for examples of:
135//! - Constructing various complex types
136//! - Computing homology of standard spaces
137//! - Working with the poset and topology interfaces
138//! - Verifying fundamental properties like ∂² = 0
139
140use std::collections::HashMap;
141
142use harness_algebra::{
143 rings::Field,
144 tensors::dynamic::{
145 compute_quotient_basis,
146 matrix::{DynamicDenseMatrix, RowMajor},
147 vector::DynamicVector,
148 },
149};
150
151use super::*;
152use crate::{
153 definitions::Topology,
154 homology::{Chain, Homology},
155 lattice::Lattice,
156 set::{Collection, Poset},
157};
158
159pub mod cubical;
160pub mod simplicial;
161
162pub use cubical::Cube;
163pub use simplicial::Simplex;
164
165/// A type alias for a simplicial complex.
166pub type SimplicialComplex = Complex<Simplex>;
167
168/// A type alias for a cubical complex.
169pub type CubicalComplex = Complex<Cube>;
170
171/// Trait for elements that can be part of a topological complex.
172///
173/// This trait captures the essential behavior needed for elements (simplices, cubes, cells, etc.)
174/// to work with the generic [`Complex<T>`] structure. It abstracts over the common operations
175/// that any type of cell complex element must support.
176///
177/// # Mathematical Foundation
178///
179/// In algebraic topology, a complex is built from cells of various dimensions with specific
180/// face relations. This trait encapsulates the core properties that any cell type must have:
181///
182/// 1. **Dimension**: Each cell has an intrinsic dimension (0 for vertices, 1 for edges, etc.)
183/// 2. **Boundary Structure**: Each cell has well-defined boundary cells (faces)
184/// 3. **Orientation**: Boundary relationships include orientation information for chain complexes
185/// 4. **Identity**: Cells can be uniquely identified when added to complexes
186/// 5. **Content Equality**: Mathematical content can be compared regardless of ID assignment
187///
188/// # Design Philosophy
189///
190/// This trait is designed to be:
191/// - **Generic**: Works with simplices, cubes, and other cell types
192/// - **Efficient**: Supports ID-based operations for large complexes
193/// - **Mathematically Correct**: Preserves orientation and boundary relationships
194/// - **Flexible**: Allows different boundary operator conventions per element type
195///
196/// # Implementation Requirements
197///
198/// Types implementing this trait must:
199/// - Be cloneable, hashable, and orderable for use in collections
200/// - Compute their own faces and boundary operators correctly
201/// - Handle ID assignment and content comparison properly
202/// - Maintain mathematical consistency in their face relationships
203///
204/// # Examples
205///
206/// ```rust
207/// use harness_space::complexes::{ComplexElement, Simplex};
208///
209/// // Create a triangle (2-simplex)
210/// let triangle = Simplex::new(2, vec![0, 1, 2]);
211///
212/// // Basic properties
213/// assert_eq!(triangle.dimension(), 2);
214/// assert_eq!(triangle.id(), None); // No ID until added to complex
215///
216/// // Compute faces (should be 3 edges)
217/// let faces = triangle.faces();
218/// assert_eq!(faces.len(), 3);
219/// assert!(faces.iter().all(|face| face.dimension() == 1));
220///
221/// // Compute boundary with orientations
222/// let boundary = triangle.boundary_with_orientations();
223/// assert_eq!(boundary.len(), 3);
224/// // Each face has orientation ±1
225/// assert!(boundary.iter().all(|(_, orient)| orient.abs() == 1));
226/// ```
227pub trait ComplexElement: Clone + std::hash::Hash + Eq + PartialOrd + Ord {
228 /// Returns the intrinsic dimension of this element.
229 ///
230 /// The dimension determines the element's place in the chain complex:
231 /// - 0-dimensional: vertices/points
232 /// - 1-dimensional: edges/curves
233 /// - 2-dimensional: faces/surfaces
234 /// - k-dimensional: k-cells
235 ///
236 /// # Mathematical Note
237 ///
238 /// For a k-dimensional element, its faces are (k-1)-dimensional, and it can be
239 /// a face of (k+1)-dimensional elements. This creates the graded structure
240 /// essential for homological computations.
241 ///
242 /// # Examples
243 ///
244 /// ```rust
245 /// # use harness_space::complexes::{ComplexElement, Simplex, Cube};
246 /// let vertex = Simplex::new(0, vec![42]);
247 /// assert_eq!(vertex.dimension(), 0);
248 ///
249 /// let edge = Cube::edge(0, 1);
250 /// assert_eq!(edge.dimension(), 1);
251 /// ```
252 fn dimension(&self) -> usize;
253
254 /// Returns all faces (boundary elements) of this element.
255 ///
256 /// For a k-dimensional element, this returns all (k-1)-dimensional faces that
257 /// form its boundary. This is the **combinatorial boundary** - it captures the
258 /// face structure without orientation information.
259 ///
260 /// # Mathematical Background
261 ///
262 /// In topology, the boundary ∂σ of a cell σ consists of all the cells in its
263 /// boundary. For example:
264 /// - Triangle faces: the three edges forming its boundary
265 /// - Tetrahedron faces: the four triangular faces
266 /// - Square faces: the four edges forming its boundary
267 ///
268 /// # Implementation Notes
269 ///
270 /// - Returned faces should have no ID assigned (will be assigned when added to complex)
271 /// - The order may matter for orientation in [`ComplexElement::boundary_with_orientations`]
272 /// - All faces must have dimension = `self.dimension() - 1`
273 /// - 0-dimensional elements return empty vector (no (-1)-dimensional faces)
274 ///
275 /// # Examples
276 ///
277 /// ```rust
278 /// # use harness_space::complexes::{ComplexElement, Simplex};
279 /// // Triangle has 3 edge faces
280 /// let triangle = Simplex::new(2, vec![0, 1, 2]);
281 /// let faces = triangle.faces();
282 /// assert_eq!(faces.len(), 3);
283 /// assert!(faces.iter().all(|f| f.dimension() == 1));
284 ///
285 /// // Vertex has no faces
286 /// let vertex = Simplex::new(0, vec![0]);
287 /// assert_eq!(vertex.faces().len(), 0);
288 /// ```
289 fn faces(&self) -> Vec<Self>;
290
291 /// Returns the faces with their correct orientation coefficients for boundary computation.
292 ///
293 /// This is the **geometric boundary operator** that includes orientation information
294 /// necessary for chain complex computations. Each face comes with an integer coefficient
295 /// (typically ±1) indicating its orientation in the boundary.
296 ///
297 /// # Mathematical Foundation
298 ///
299 /// In algebraic topology, the boundary operator ∂ₖ: Cₖ → Cₖ₋₁ is defined as:
300 ///
301 /// ```text
302 /// ∂ₖ(σ) = Σᵢ (-1)ⁱ τᵢ
303 /// ```
304 ///
305 /// where the τᵢ are the faces of σ with appropriate orientation signs. The key
306 /// property is that ∂² = 0 (boundary of boundary is zero), which requires
307 /// careful orientation handling.
308 ///
309 /// # Orientation Conventions
310 ///
311 /// Different element types may use different orientation conventions:
312 /// - **Simplicial**: Alternating signs based on vertex position
313 /// - **Cubical**: Signs based on coordinate directions
314 /// - **General CW**: Depends on attaching maps
315 ///
316 /// # Return Format
317 ///
318 /// Returns `Vec<(face, orientation)>` where:
319 /// - `face`: A (k-1)-dimensional face element (without ID)
320 /// - `orientation`: Integer coefficient (usually ±1, could be 0)
321 ///
322 /// # Examples
323 ///
324 /// ```rust
325 /// # use harness_space::complexes::{ComplexElement, Simplex};
326 /// let triangle = Simplex::new(2, vec![0, 1, 2]);
327 /// let boundary = triangle.boundary_with_orientations();
328 ///
329 /// // Triangle boundary: [v₁,v₂] - [v₀,v₂] + [v₀,v₁]
330 /// assert_eq!(boundary.len(), 3);
331 /// assert_eq!(boundary[0].1, 1); // +[v₁,v₂]
332 /// assert_eq!(boundary[1].1, -1); // -[v₀,v₂]
333 /// assert_eq!(boundary[2].1, 1); // +[v₀,v₁]
334 /// ```
335 fn boundary_with_orientations(&self) -> Vec<(Self, i32)>;
336
337 /// Returns the ID if this element has been assigned to a complex, `None` otherwise.
338 ///
339 /// IDs are automatically assigned when elements are added to a [`Complex`] via
340 /// [`Complex::join_element`]. They serve as unique identifiers for efficient
341 /// storage and lookup operations.
342 ///
343 /// # ID Assignment Lifecycle
344 ///
345 /// 1. **Created**: Element starts with `id() = None`
346 /// 2. **Added**: Complex assigns unique ID via [`ComplexElement::with_id`]
347 /// 3. **Stored**: Element with ID is stored in complex's HashMap
348 /// 4. **Referenced**: ID used for lattice operations and lookups
349 ///
350 /// # Examples
351 ///
352 /// ```rust
353 /// # use harness_space::complexes::{Complex, Simplex};
354 /// let simplex = Simplex::new(1, vec![0, 1]);
355 /// assert_eq!(simplex.id(), None);
356 ///
357 /// let mut complex = Complex::new();
358 /// let added = complex.join_element(simplex);
359 /// assert!(added.id().is_some());
360 /// ```
361 fn id(&self) -> Option<usize>;
362
363 /// Checks if this element has the same mathematical content as another.
364 ///
365 /// This comparison ignores ID assignment and focuses purely on the mathematical
366 /// structure of the elements. It's used for deduplication when adding elements
367 /// to complexes.
368 ///
369 /// # Mathematical Equality
370 ///
371 /// Two elements are considered to have the same content if they represent
372 /// the same mathematical object, regardless of:
373 /// - ID assignment (internal bookkeeping)
374 /// - Order of discovery (when added to complex)
375 /// - Memory location or other implementation details
376 ///
377 /// # Usage in Complexes
378 ///
379 /// When [`Complex::join_element`] is called, it first checks if an element
380 /// with the same content already exists using this method. If found, it
381 /// returns the existing element rather than creating a duplicate.
382 ///
383 /// # Examples
384 ///
385 /// ```rust
386 /// # use harness_space::complexes::{ComplexElement, Simplex};
387 /// let simplex1 = Simplex::new(1, vec![0, 1]);
388 /// let simplex2 = Simplex::new(1, vec![0, 1]);
389 /// let simplex3 = simplex1.clone().with_id(42);
390 ///
391 /// assert!(simplex1.same_content(&simplex2)); // Same mathematical content
392 /// assert!(simplex1.same_content(&simplex3)); // ID differences ignored
393 ///
394 /// let different = Simplex::new(1, vec![0, 2]);
395 /// assert!(!simplex1.same_content(&different)); // Different content
396 /// ```
397 fn same_content(&self, other: &Self) -> bool;
398
399 /// Creates a new element with the same content but a specific ID.
400 ///
401 /// This is used internally by [`Complex`] to assign IDs when adding elements.
402 /// The returned element should be identical in all mathematical properties
403 /// but have the specified ID assigned.
404 ///
405 /// # Implementation Requirements
406 ///
407 /// The returned element must satisfy:
408 /// - `result.same_content(self) == true`
409 /// - `result.id() == Some(new_id)`
410 /// - All other properties unchanged (dimension, faces, etc.)
411 ///
412 /// # Usage
413 ///
414 /// This method is primarily used internally by the complex management system.
415 /// Users typically don't need to call it directly.
416 ///
417 /// # Examples
418 ///
419 /// ```rust
420 /// # use harness_space::complexes::{ComplexElement, Simplex};
421 /// let original = Simplex::new(1, vec![0, 1]);
422 /// assert_eq!(original.id(), None);
423 ///
424 /// let with_id = original.with_id(42);
425 /// assert_eq!(with_id.id(), Some(42));
426 /// assert!(original.same_content(&with_id));
427 /// assert_eq!(original.dimension(), with_id.dimension());
428 /// ```
429 fn with_id(&self, new_id: usize) -> Self;
430}
431
432/// A generic topological complex that can work with any type implementing [`ComplexElement`].
433///
434/// This structure provides a unified framework for working with various types of cell complexes
435/// (simplicial, cubical, etc.) while maintaining the essential topological and algebraic
436/// properties needed for homological computations.
437///
438/// # Mathematical Foundation
439///
440/// A **cell complex** K is a topological space built by attaching cells of various dimensions
441/// according to specific rules:
442///
443/// 1. **Closure Property**: If σ ∈ K, then all faces of σ are also in K
444/// 2. **Face Relations**: Form a partial order where τ ≤ σ means "τ is a face of σ"
445/// 3. **Dimension Stratification**: K = K⁰ ∪ K¹ ∪ K² ∪ ... where Kⁱ contains all i-cells
446/// 4. **Chain Complex Structure**: Boundary operators ∂ᵢ: Cᵢ → Cᵢ₋₁ with ∂² = 0
447///
448/// # Implementation Architecture
449///
450/// This implementation uses a **dual-structure approach**:
451///
452/// ```text
453/// ┌─────────────────┐ ┌──────────────────┐
454/// │ attachment_ │ │ elements: │
455/// │ lattice: │◄──►│ HashMap<usize,T> │
456/// │ Lattice<usize> │ │ │
457/// └─────────────────┘ └──────────────────┘
458/// ▲ ▲
459/// │ IDs only │ Full elements
460/// ▼ ▼
461/// Fast operations Rich operations
462/// ```
463///
464/// - **Elements HashMap**: Stores actual elements indexed by unique IDs
465/// - **Attachment Lattice**: Tracks face relationships using IDs for efficiency
466/// - **ID Management**: Automatic assignment with deduplication support
467///
468/// # Key Properties
469///
470/// ## Closure Property Enforcement
471///
472/// The [`Complex::join_element`] method ensures closure: adding any element automatically
473/// includes all its faces. This maintains the fundamental property that distinguishes
474/// complexes from arbitrary cell collections.
475///
476/// ## Efficient Face Queries
477///
478/// Face relationships are stored in a [`Lattice<usize>`] structure, enabling:
479/// - O(1) face/coface lookups after preprocessing
480/// - Efficient upset/downset computations
481/// - Support for meet/join operations on cells
482///
483/// ## Deduplication
484///
485/// Elements with identical mathematical content are automatically deduplicated
486/// using [`ComplexElement::same_content`], preventing redundant storage and
487/// maintaining well-defined structure.
488///
489/// # Usage Patterns
490///
491/// ## Basic Construction
492///
493/// ```rust
494/// use harness_space::complexes::{Complex, Simplex};
495///
496/// let mut complex = Complex::new();
497/// let triangle = Simplex::new(2, vec![0, 1, 2]);
498/// let added = complex.join_element(triangle);
499///
500/// // Automatically includes all faces: 1 triangle + 3 edges + 3 vertices
501/// assert_eq!(complex.elements_of_dimension(2).len(), 1);
502/// assert_eq!(complex.elements_of_dimension(1).len(), 3);
503/// assert_eq!(complex.elements_of_dimension(0).len(), 3);
504/// ```
505///
506/// ## Homology Computation
507///
508/// ```rust
509/// use harness_algebra::algebras::boolean::Boolean;
510/// use harness_space::{
511/// complexes::{Simplex, SimplicialComplex},
512/// prelude::*,
513/// };
514///
515/// let mut complex = SimplicialComplex::new();
516///
517/// // Create a circle (1-dimensional hole)
518/// let edge1 = Simplex::new(1, vec![0, 1]);
519/// let edge2 = Simplex::new(1, vec![1, 2]);
520/// let edge3 = Simplex::new(1, vec![2, 0]);
521/// complex.join_element(edge1);
522/// complex.join_element(edge2);
523/// complex.join_element(edge3);
524///
525/// let h1 = complex.homology::<Boolean>(1);
526/// assert_eq!(h1.betti_number, 1); // One 1D hole
527/// ```
528///
529/// ## Working with Face Relations
530///
531/// ```rust
532/// use harness_space::{
533/// complexes::{Simplex, SimplicialComplex},
534/// prelude::*,
535/// };
536///
537/// let mut complex = SimplicialComplex::new();
538/// let triangle = Simplex::new(2, vec![0, 1, 2]);
539/// let added = complex.join_element(triangle);
540///
541/// // Query face relationships
542/// let faces = complex.faces(&added); // Direct faces only
543/// let cofaces = complex.cofaces(&added); // Direct cofaces only
544/// let downset = complex.downset(added); // All faces (transitive)
545/// ```
546///
547/// # Performance Characteristics
548///
549/// - **Element Access**: O(1) by ID, O(n) by content search
550/// - **Face Queries**: O(1) for direct faces, O(k) for k-dimensional queries
551/// - **Adding Elements**: O(f) where f is the number of faces to add
552/// - **Homology**: O(n³) where n is the number of elements in relevant dimensions
553///
554/// # Type Parameters
555///
556/// * `T`: The element type, must implement [`ComplexElement`]
557///
558/// # Examples
559///
560/// See the extensive test suite for examples including:
561/// - Construction of standard complexes (simplicial, cubical)
562/// - Homology computations for various topological spaces
563/// - Integration with poset and topology interfaces
564#[derive(Debug, Clone)]
565pub struct Complex<T: ComplexElement> {
566 /// The attachment relationships between elements, represented as a lattice of element IDs.
567 ///
568 /// This lattice encodes the face relation: if element `a` is a face of element `b`,
569 /// then `attachment_lattice.leq(a.id(), b.id())` returns `Some(true)`.
570 ///
571 /// Using IDs rather than full elements provides:
572 /// - **Memory efficiency**: IDs are much smaller than full elements
573 /// - **Performance**: Integer comparisons are faster than element comparisons
574 /// - **Flexibility**: Lattice operations independent of element type
575 pub attachment_lattice: Lattice<usize>,
576
577 /// A map storing all elements in the complex, keyed by their assigned ID.
578 ///
579 /// This provides:
580 /// - **Fast lookup**: O(1) access to elements by ID
581 /// - **Rich operations**: Access to full element data and methods
582 /// - **Content queries**: Iteration over elements by dimension, type, etc.
583 pub elements: HashMap<usize, T>,
584
585 /// The counter for the next available unique element identifier.
586 ///
587 /// This ensures that:
588 /// - Each element gets a unique ID when added
589 /// - IDs are assigned sequentially for predictable behavior
590 /// - The complex can manage arbitrary numbers of elements
591 pub next_id: usize,
592}
593
594impl<T: ComplexElement> Complex<T> {
595 /// Creates a new, empty complex.
596 ///
597 /// The empty complex contains no elements and has trivial homology:
598 /// - H₀ = 0 (no connected components)
599 /// - Hₖ = 0 for all k > 0 (no higher-dimensional features)
600 ///
601 /// # Examples
602 ///
603 /// ```rust
604 /// use harness_space::{
605 /// complexes::{Complex, Simplex},
606 /// prelude::*,
607 /// };
608 ///
609 /// let complex: Complex<Simplex> = Complex::new();
610 /// assert!(complex.is_empty());
611 /// assert_eq!(complex.max_dimension(), 0);
612 /// ```
613 pub fn new() -> Self {
614 Self {
615 attachment_lattice: Lattice::new(),
616 elements: HashMap::new(),
617 next_id: 0,
618 }
619 }
620
621 /// Adds an element to the complex along with all its faces.
622 ///
623 /// This is the **fundamental operation** for building complexes. It ensures the closure
624 /// property by recursively adding all faces of the given element. If an element with
625 /// equivalent mathematical content already exists, returns the existing element without
626 /// modification.
627 ///
628 /// # Mathematical Foundation
629 ///
630 /// In topology, a complex must satisfy the **closure property**:
631 ///
632 /// > If σ ∈ K, then ∂σ ⊆ K
633 ///
634 /// This method enforces this property by:
635 /// 1. Computing all faces of the input element via [`ComplexElement::faces`]
636 /// 2. Recursively adding each face (which adds their faces, etc.)
637 /// 3. Establishing face relationships in the attachment lattice
638 /// 4. Deduplicating based on mathematical content
639 ///
640 /// # Algorithm
641 ///
642 /// ```text
643 /// join_element(σ):
644 /// 1. Check if equivalent element already exists → return existing
645 /// 2. Assign ID to σ (reuse existing ID if valid, else assign next_id)
646 /// 3. For each face τ in faces(σ):
647 /// added_τ ← join_element(τ) // Recursive call
648 /// Add relation: added_τ ≤ σ to lattice
649 /// 4. Store σ in elements map
650 /// 5. Return σ with assigned ID
651 /// ```
652 ///
653 /// # ID Assignment Strategy
654 ///
655 /// - **No ID**: Assigns `next_id` and increments counter
656 /// - **Has unused ID**: Preserves existing ID if not taken
657 /// - **Has conflicting ID**: Assigns new ID to avoid conflicts
658 ///
659 /// # Deduplication Logic
660 ///
661 /// Uses [`ComplexElement::same_content`] to check for existing equivalent elements.
662 /// This ensures that mathematically identical elements (regardless of ID) are not
663 /// duplicated in the complex.
664 ///
665 /// # Face Relationship Management
666 ///
667 /// Establishes `face_id ≤ element_id` relationships in the attachment lattice for
668 /// all direct faces. Transitive relationships are computed automatically by the
669 /// lattice structure.
670 ///
671 /// # Return Value
672 ///
673 /// Returns the element as it exists in the complex (with assigned ID). This may be:
674 /// - The input element with a newly assigned ID
675 /// - An existing equivalent element if content matches
676 ///
677 /// # Performance Notes
678 ///
679 /// - **Time**: O(f) where f is the total number of faces to add (including recursive)
680 /// - **Space**: O(n) additional storage where n is the number of new elements
681 /// - **Deduplication**: O(k) check where k is the number of existing elements
682 ///
683 /// # Examples
684 ///
685 /// ## Basic Usage
686 ///
687 /// ```rust
688 /// use harness_space::complexes::{Complex, Simplex};
689 ///
690 /// let mut complex = Complex::new();
691 /// let triangle = Simplex::new(2, vec![0, 1, 2]);
692 ///
693 /// let added = complex.join_element(triangle);
694 /// assert!(added.id().is_some());
695 ///
696 /// // Complex now contains triangle + 3 edges + 3 vertices
697 /// assert_eq!(complex.elements_of_dimension(2).len(), 1);
698 /// assert_eq!(complex.elements_of_dimension(1).len(), 3);
699 /// assert_eq!(complex.elements_of_dimension(0).len(), 3);
700 /// ```
701 ///
702 /// ## Deduplication
703 ///
704 /// ```rust
705 /// # use harness_space::complexes::{Complex, Simplex};
706 /// # let mut complex = Complex::new();
707 /// let edge1 = Simplex::new(1, vec![0, 1]);
708 /// let edge2 = Simplex::new(1, vec![0, 1]); // Same content
709 ///
710 /// let added1 = complex.join_element(edge1);
711 /// let added2 = complex.join_element(edge2);
712 ///
713 /// // Returns same element (by ID)
714 /// assert_eq!(added1.id(), added2.id());
715 /// assert_eq!(complex.elements_of_dimension(1).len(), 1);
716 /// ```
717 ///
718 /// ## Building Complex Structures
719 ///
720 /// ```rust
721 /// # use harness_space::complexes::{Complex, Simplex};
722 /// let mut complex = Complex::new();
723 ///
724 /// // Add multiple triangles sharing edges
725 /// let triangle1 = Simplex::new(2, vec![0, 1, 2]);
726 /// let triangle2 = Simplex::new(2, vec![1, 2, 3]);
727 ///
728 /// complex.join_element(triangle1);
729 /// complex.join_element(triangle2);
730 ///
731 /// // Shared edge [1,2] is not duplicated
732 /// assert_eq!(complex.elements_of_dimension(2).len(), 2); // 2 triangles
733 /// assert_eq!(complex.elements_of_dimension(1).len(), 5); // 5 unique edges
734 /// assert_eq!(complex.elements_of_dimension(0).len(), 4); // 4 vertices
735 /// ```
736 pub fn join_element(&mut self, element: T) -> T {
737 // Check if we already have this element (by mathematical content)
738 if let Some(existing) = self.find_equivalent_element(&element) {
739 return existing;
740 }
741
742 // Assign a new ID to this element
743 let element_with_id =
744 if element.id().is_some() && !self.elements.contains_key(&element.id().unwrap()) {
745 // Element already has an ID and it's not taken
746 if element.id().unwrap() >= self.next_id {
747 self.next_id = element.id().unwrap() + 1;
748 }
749 element
750 } else {
751 // Assign a new ID
752 let new_id = self.next_id;
753 self.next_id += 1;
754 element.with_id(new_id)
755 };
756
757 let mut face_ids = Vec::new();
758 for face in element_with_id.faces() {
759 let added_face = self.join_element(face);
760 face_ids.push(added_face.id().unwrap()); // Safe because we just added it
761 }
762
763 let element_id = element_with_id.id().unwrap();
764 self.attachment_lattice.add_element(element_id);
765
766 for face_id in face_ids {
767 self.attachment_lattice.add_relation(face_id, element_id);
768 }
769
770 self.elements.insert(element_id, element_with_id.clone());
771 element_with_id
772 }
773
774 /// Finds an element in the complex with equivalent mathematical content.
775 ///
776 /// This is used internally by [`join_element`] for deduplication. Returns the first
777 /// element found with matching content, or `None` if no match exists.
778 ///
779 /// # Performance
780 ///
781 /// This performs a linear search through all elements, so it's O(n) where n is the
782 /// total number of elements in the complex. For large complexes, this can be a
783 /// bottleneck in construction.
784 ///
785 /// # Future Optimizations
786 ///
787 /// Potential improvements could include:
788 /// - Content-based hashing for faster lookup
789 /// - Spatial indexing for geometric elements
790 /// - Dimension-stratified search to reduce search space
791 fn find_equivalent_element(&self, element: &T) -> Option<T> {
792 self.elements.values().find(|existing| element.same_content(existing)).cloned()
793 }
794
795 /// Retrieves an element by its unique ID.
796 ///
797 /// Returns `None` if no element with the given ID exists in the complex.
798 /// This is the primary method for accessing elements when you have their ID.
799 ///
800 /// # Performance
801 ///
802 /// O(1) HashMap lookup.
803 ///
804 /// # Examples
805 ///
806 /// ```rust
807 /// # use harness_space::complexes::{Complex, Simplex};
808 /// # let mut complex = Complex::new();
809 /// let simplex = Simplex::new(1, vec![0, 1]);
810 /// let added = complex.join_element(simplex);
811 /// let id = added.id().unwrap();
812 ///
813 /// let retrieved = complex.get_element(id).unwrap();
814 /// assert!(added.same_content(retrieved));
815 /// ```
816 pub fn get_element(&self, id: usize) -> Option<&T> { self.elements.get(&id) }
817
818 /// Returns all elements of a specific dimension.
819 ///
820 /// This is useful for:
821 /// - Constructing basis sets for homology computations
822 /// - Analyzing the dimensional structure of the complex
823 /// - Iterating over elements by type (vertices, edges, faces, etc.)
824 ///
825 /// # Performance
826 ///
827 /// O(n) where n is the total number of elements, as it must check the dimension
828 /// of every element.
829 ///
830 /// # Examples
831 ///
832 /// ```rust
833 /// # use harness_space::complexes::{Complex, Simplex};
834 /// # let mut complex = Complex::new();
835 /// # let triangle = Simplex::new(2, vec![0, 1, 2]);
836 /// # complex.join_element(triangle);
837 ///
838 /// let vertices = complex.elements_of_dimension(0); // All 0-cells
839 /// let edges = complex.elements_of_dimension(1); // All 1-cells
840 /// let faces = complex.elements_of_dimension(2); // All 2-cells
841 ///
842 /// assert_eq!(vertices.len(), 3);
843 /// assert_eq!(edges.len(), 3);
844 /// assert_eq!(faces.len(), 1);
845 /// ```
846 pub fn elements_of_dimension(&self, dimension: usize) -> Vec<T> {
847 self.elements.values().filter(|element| element.dimension() == dimension).cloned().collect()
848 }
849
850 /// Returns the maximum dimension of any element in the complex.
851 ///
852 /// For an empty complex, returns 0. This is useful for determining the
853 /// "dimension" of the complex as a topological space.
854 ///
855 /// # Examples
856 ///
857 /// ```rust
858 /// # use harness_space::complexes::{Complex, Simplex};
859 /// # let mut complex = Complex::new();
860 /// assert_eq!(complex.max_dimension(), 0); // Empty complex
861 ///
862 /// let triangle = Simplex::new(2, vec![0, 1, 2]);
863 /// complex.join_element(triangle);
864 /// assert_eq!(complex.max_dimension(), 2); // 2D complex
865 /// ```
866 pub fn max_dimension(&self) -> usize {
867 self.elements.values().map(ComplexElement::dimension).max().unwrap_or(0)
868 }
869
870 /// Returns the direct faces of an element within this complex.
871 ///
872 /// This differs from [`ComplexElement::faces`] in that it returns elements that
873 /// actually exist in the complex (with assigned IDs) rather than abstract face
874 /// descriptions.
875 ///
876 /// # Relationship to Lattice Operations
877 ///
878 /// This is equivalent to finding the immediate predecessors of the element in
879 /// the face lattice, but returns the full element objects rather than just IDs.
880 ///
881 /// # Examples
882 ///
883 /// ```rust
884 /// # use harness_space::complexes::{Complex, Simplex};
885 /// # let mut complex = Complex::new();
886 /// let triangle = Simplex::new(2, vec![0, 1, 2]);
887 /// let added_triangle = complex.join_element(triangle);
888 ///
889 /// let faces = complex.faces(&added_triangle);
890 /// assert_eq!(faces.len(), 3); // Three edges
891 /// assert!(faces.iter().all(|face| face.dimension() == 1));
892 /// assert!(faces.iter().all(|face| face.id().is_some()));
893 /// ```
894 pub fn faces(&self, element: &T) -> Vec<T> {
895 element.id().map_or_else(Vec::new, |id| {
896 self
897 .attachment_lattice
898 .predecessors(id)
899 .into_iter()
900 .filter_map(|face_id| self.get_element(face_id).cloned())
901 .collect()
902 })
903 }
904
905 /// Returns the direct cofaces of an element within this complex.
906 ///
907 /// Cofaces are elements that have the given element as a face. This is the
908 /// "upward" direction in the face lattice.
909 ///
910 /// # Examples
911 ///
912 /// ```rust
913 /// # use harness_space::complexes::{Complex, Simplex};
914 /// # let mut complex = Complex::new();
915 /// # let triangle = Simplex::new(2, vec![0, 1, 2]);
916 /// # let added_triangle = complex.join_element(triangle);
917 /// # let edges = complex.elements_of_dimension(1);
918 /// # let edge = edges[0].clone();
919 ///
920 /// let cofaces = complex.cofaces(&edge);
921 /// assert_eq!(cofaces.len(), 1); // Edge is face of triangle
922 /// assert!(cofaces[0].same_content(&added_triangle));
923 /// ```
924 pub fn cofaces(&self, element: &T) -> Vec<T> {
925 element.id().map_or_else(Vec::new, |id| {
926 self
927 .attachment_lattice
928 .successors(id)
929 .into_iter()
930 .filter_map(|coface_id| self.get_element(coface_id).cloned())
931 .collect()
932 })
933 }
934
935 /// Computes the k-dimensional homology of the complex over a field F.
936 ///
937 /// Homology measures the "holes" in a topological space at different dimensions:
938 /// - **H₀**: Connected components (0-dimensional holes)
939 /// - **H₁**: Loops/cycles (1-dimensional holes)
940 /// - **H₂**: Voids/cavities (2-dimensional holes)
941 /// - **Hₖ**: k-dimensional holes (higher-dimensional features)
942 ///
943 /// # Mathematical Foundation
944 ///
945 /// Homology is defined as the quotient of cycles by boundaries:
946 ///
947 /// ```text
948 /// Hₖ(K) = Zₖ(K) / Bₖ(K) = ker(∂ₖ) / im(∂ₖ₊₁)
949 /// ```
950 ///
951 /// Where:
952 /// - **Zₖ(K) = ker(∂ₖ)**: k-cycles (chains with no boundary)
953 /// - **Bₖ(K) = im(∂ₖ₊₁)**: k-boundaries (boundaries of (k+1)-chains)
954 /// - **∂ₖ**: Boundary operator from k-chains to (k-1)-chains
955 ///
956 /// The key insight is that "holes" are cycles that are not boundaries of higher-dimensional
957 /// chains.
958 ///
959 /// # Algorithm
960 ///
961 /// 1. **Compute Cycles**: Find kernel of boundary operator ∂ₖ: Cₖ → Cₖ₋₁
962 /// 2. **Compute Boundaries**: Find image of boundary operator ∂ₖ₊₁: Cₖ₊₁ → Cₖ
963 /// 3. **Quotient Space**: Compute basis for quotient space Zₖ/Bₖ
964 /// 4. **Return Homology**: Package result with Betti number and generators
965 ///
966 /// # Special Cases
967 ///
968 /// - **k = 0**: H₀ measures connected components. Z₀ = C₀ (all 0-chains are cycles)
969 /// - **Empty Complex**: All homology groups are trivial (Hₖ = 0)
970 /// - **No k-elements**: Returns trivial homology for that dimension
971 ///
972 /// # Field Dependency
973 ///
974 /// The choice of coefficient field F affects the result:
975 /// - **ℤ/2ℤ (Boolean)**: Ignores orientation, counts mod 2
976 /// - **ℚ (Rationals)**: Full torsion-free homology
977 /// - **ℤ/pℤ (Prime fields)**: Reveals p-torsion in homology
978 ///
979 /// # Performance
980 ///
981 /// - **Time**: O(n³) where n is the number of k-dimensional elements
982 /// - **Space**: O(n²) for storing boundary matrices
983 /// - **Bottleneck**: Matrix kernel and image computations
984 ///
985 /// # Return Value
986 ///
987 /// Returns a [`Homology`] object containing:
988 /// - `dimension`: The dimension k being computed
989 /// - `betti_number`: The rank of Hₖ (number of independent k-dimensional holes)
990 /// - `homology_generators`: Basis vectors representing the homology classes
991 ///
992 /// # Examples
993 ///
994 /// ## Circle (1-dimensional hole)
995 ///
996 /// ```rust
997 /// use harness_algebra::algebras::boolean::Boolean;
998 /// use harness_space::complexes::{Complex, Simplex};
999 ///
1000 /// let mut complex = Complex::new();
1001 ///
1002 /// // Create a triangle boundary (3 edges forming a cycle)
1003 /// let edge1 = Simplex::new(1, vec![0, 1]);
1004 /// let edge2 = Simplex::new(1, vec![1, 2]);
1005 /// let edge3 = Simplex::new(1, vec![2, 0]);
1006 /// complex.join_element(edge1);
1007 /// complex.join_element(edge2);
1008 /// complex.join_element(edge3);
1009 ///
1010 /// let h0 = complex.homology::<Boolean>(0);
1011 /// let h1 = complex.homology::<Boolean>(1);
1012 ///
1013 /// assert_eq!(h0.betti_number, 1); // One connected component
1014 /// assert_eq!(h1.betti_number, 1); // One 1-dimensional hole
1015 /// ```
1016 ///
1017 /// ## Filled Triangle (no holes)
1018 ///
1019 /// ```rust
1020 /// # use harness_algebra::algebras::boolean::Boolean;
1021 /// # use harness_space::complexes::{Complex, Simplex};
1022 /// # let mut complex = Complex::new();
1023 ///
1024 /// let triangle = Simplex::new(2, vec![0, 1, 2]);
1025 /// complex.join_element(triangle);
1026 ///
1027 /// let h0 = complex.homology::<Boolean>(0);
1028 /// let h1 = complex.homology::<Boolean>(1);
1029 ///
1030 /// assert_eq!(h0.betti_number, 1); // One connected component
1031 /// assert_eq!(h1.betti_number, 0); // No 1D holes (filled)
1032 /// ```
1033 ///
1034 /// ## Sphere Surface (2-dimensional hole)
1035 ///
1036 /// ```rust
1037 /// # use harness_algebra::algebras::boolean::Boolean;
1038 /// # use harness_space::complexes::{Complex, Simplex};
1039 /// # let mut complex = Complex::new();
1040 ///
1041 /// // Tetrahedron boundary (4 triangular faces)
1042 /// let face1 = Simplex::new(2, vec![0, 1, 2]);
1043 /// let face2 = Simplex::new(2, vec![0, 1, 3]);
1044 /// let face3 = Simplex::new(2, vec![0, 2, 3]);
1045 /// let face4 = Simplex::new(2, vec![1, 2, 3]);
1046 /// complex.join_element(face1);
1047 /// complex.join_element(face2);
1048 /// complex.join_element(face3);
1049 /// complex.join_element(face4);
1050 ///
1051 /// let h2 = complex.homology::<Boolean>(2);
1052 /// assert_eq!(h2.betti_number, 1); // One 2-dimensional hole (sphere interior)
1053 /// ```
1054 pub fn homology<F: Field + Copy>(&self, k: usize) -> Homology<F> {
1055 let k_elements = {
1056 let mut elements = self.elements_of_dimension(k);
1057 elements.sort_unstable();
1058 elements
1059 };
1060
1061 if k_elements.is_empty() {
1062 return Homology::trivial(k);
1063 }
1064
1065 let cycles = if k == 0 {
1066 // Z₀ = C₀ (kernel of ∂₀: C₀ -> C₋₁ is C₀ itself).
1067 let num_0_elements = k_elements.len();
1068 let mut basis: Vec<DynamicVector<F>> = Vec::with_capacity(num_0_elements);
1069 for i in 0..num_0_elements {
1070 let mut v_data = vec![F::zero(); num_0_elements];
1071 v_data[i] = F::one();
1072 basis.push(DynamicVector::new(v_data));
1073 }
1074 basis
1075 } else {
1076 let boundary_k = self.get_boundary_matrix::<F>(k);
1077 boundary_k.kernel()
1078 };
1079
1080 let boundary_k_plus_1 = self.get_boundary_matrix::<F>(k + 1);
1081 let boundaries = boundary_k_plus_1.image();
1082
1083 let quotient_basis_vectors = compute_quotient_basis(&boundaries, &cycles);
1084
1085 Homology {
1086 dimension: k,
1087 betti_number: quotient_basis_vectors.len(),
1088 homology_generators: quotient_basis_vectors,
1089 }
1090 }
1091
1092 /// Constructs the boundary matrix ∂ₖ: Cₖ → Cₖ₋₁ for the k-th boundary operator.
1093 ///
1094 /// The boundary matrix is the matrix representation of the linear map that takes
1095 /// k-dimensional chains to their (k-1)-dimensional boundaries. This is the
1096 /// fundamental building block for homology computations.
1097 ///
1098 /// # Mathematical Definition
1099 ///
1100 /// For a k-dimensional element σ, its boundary ∂σ is a formal sum of (k-1)-dimensional
1101 /// faces with appropriate orientation coefficients:
1102 ///
1103 /// ```text
1104 /// ∂ₖ(σ) = Σᵢ aᵢ τᵢ
1105 /// ```
1106 ///
1107 /// where τᵢ are the faces of σ and aᵢ ∈ F are the orientation coefficients.
1108 ///
1109 /// # Matrix Structure
1110 ///
1111 /// The resulting matrix has:
1112 /// - **Rows**: Indexed by (k-1)-dimensional elements (codomain basis)
1113 /// - **Columns**: Indexed by k-dimensional elements (domain basis)
1114 /// - **Entry (i,j)**: Coefficient of codomain element i in ∂(domain element j)
1115 ///
1116 /// # Element Ordering
1117 ///
1118 /// Both domain and codomain elements are sorted using their natural ordering
1119 /// (from [`Ord`] implementation). This ensures:
1120 /// - Deterministic matrix construction
1121 /// - Consistent results across runs
1122 /// - Predictable basis element correspondence
1123 ///
1124 /// # Boundary Operator Properties
1125 ///
1126 /// The matrix satisfies the fundamental property **∂² = 0**, meaning that
1127 /// `boundary_matrix(k+1) * boundary_matrix(k) = 0`. This is essential for
1128 /// the chain complex structure and homology computations.
1129 ///
1130 /// # Special Cases
1131 ///
1132 /// - **k = 0**: Returns empty matrix (0-dimensional elements have no boundary)
1133 /// - **No k-elements**: Returns matrix with correct row count but no columns
1134 /// - **No (k-1)-elements**: Returns matrix with correct column count but no rows
1135 ///
1136 /// # Implementation Details
1137 ///
1138 /// This method uses the [`ComplexElement::boundary_with_orientations`] method
1139 /// to get the oriented boundary of each element, then constructs the matrix
1140 /// by mapping faces to their positions in the codomain basis.
1141 ///
1142 /// # Performance
1143 ///
1144 /// - **Time**: O(nf) where n is the number of k-elements and f is the average number of faces
1145 /// - **Space**: O(nm) where m is the number of (k-1)-elements
1146 /// - **Optimized**: Only computes non-zero entries
1147 ///
1148 /// # Examples
1149 ///
1150 /// ## Triangle Boundary Matrix
1151 ///
1152 /// ```rust
1153 /// use harness_algebra::algebras::boolean::Boolean;
1154 /// use harness_space::complexes::{Complex, Simplex};
1155 ///
1156 /// let mut complex = Complex::new();
1157 /// let triangle = Simplex::new(2, vec![0, 1, 2]);
1158 /// complex.join_element(triangle);
1159 ///
1160 /// // Get boundary matrix ∂₂: C₂ → C₁ (triangles → edges)
1161 /// let boundary_2 = complex.get_boundary_matrix::<Boolean>(2);
1162 ///
1163 /// // Should be 3×1 matrix (3 edges, 1 triangle)
1164 /// assert_eq!(boundary_2.num_rows(), 3); // 3 edges
1165 /// assert_eq!(boundary_2.num_cols(), 1); // 1 triangle
1166 /// ```
1167 ///
1168 /// ## Edge Boundary Matrix
1169 ///
1170 /// ```rust
1171 /// # use harness_algebra::algebras::boolean::Boolean;
1172 /// # use harness_space::complexes::{Complex, Simplex};
1173 /// # let mut complex = Complex::new();
1174 /// let edge = Simplex::new(1, vec![0, 1]);
1175 /// complex.join_element(edge);
1176 ///
1177 /// // Get boundary matrix ∂₁: C₁ → C₀ (edges → vertices)
1178 /// let boundary_1 = complex.get_boundary_matrix::<Boolean>(1);
1179 ///
1180 /// // Should be 2×1 matrix (2 vertices, 1 edge)
1181 /// assert_eq!(boundary_1.num_rows(), 2); // 2 vertices
1182 /// assert_eq!(boundary_1.num_cols(), 1); // 1 edge
1183 /// ```
1184 pub fn get_boundary_matrix<F: Field + Copy>(&self, k: usize) -> DynamicDenseMatrix<F, RowMajor>
1185 where T: ComplexElement {
1186 let codomain_basis = if k == 0 {
1187 Vec::new()
1188 } else {
1189 let mut elements = self.elements_of_dimension(k - 1);
1190 elements.sort_unstable();
1191 elements
1192 };
1193
1194 let domain_basis = {
1195 let mut elements = self.elements_of_dimension(k);
1196 elements.sort_unstable();
1197 elements
1198 };
1199
1200 let mut matrix = DynamicDenseMatrix::<F, RowMajor>::new();
1201
1202 if domain_basis.is_empty() {
1203 // Create a matrix with appropriate number of rows but no columns
1204 for _ in 0..codomain_basis.len() {
1205 matrix.append_row(DynamicVector::new(Vec::new()));
1206 }
1207 return matrix;
1208 }
1209
1210 // Create a map from elements to their position in the codomain basis
1211 let basis_map_for_codomain: HashMap<&T, usize> =
1212 codomain_basis.iter().enumerate().map(|(i, s)| (s, i)).collect();
1213 let num_codomain_elements = codomain_basis.len();
1214
1215 for element_from_domain in &domain_basis {
1216 // Compute boundary using the Topology trait implementation
1217 let boundary_chain: Chain<Self, F> = self.boundary(element_from_domain);
1218
1219 // Convert the chain to a coefficient vector
1220 let col_vector =
1221 boundary_chain.to_coeff_vector(&basis_map_for_codomain, num_codomain_elements);
1222 matrix.append_column(&col_vector);
1223 }
1224
1225 matrix
1226 }
1227}
1228
1229impl<T: ComplexElement> Default for Complex<T> {
1230 fn default() -> Self { Self::new() }
1231}
1232
1233// =============================================================================
1234// TRAIT IMPLEMENTATIONS
1235// =============================================================================
1236//
1237// The following implementations make Complex<T> integrate seamlessly with the
1238// broader algebraic and topological framework:
1239//
1240// - Collection: Basic containment and emptiness queries
1241// - Poset: Face relation partial order operations (≤, upset, downset, etc.)
1242// - Topology: Neighborhood and boundary operations for topological computations
1243//
1244// These implementations enable Complex<T> to work with:
1245// - Generic algorithms that operate on posets or topological spaces
1246// - Homology computations via the Topology trait boundary operator
1247// - Sheaf computations that require poset structure
1248// - General lattice-theoretic operations
1249
1250/// Implementation of [`Collection`] for complexes.
1251///
1252/// Provides basic set-theoretic operations for checking element membership
1253/// and complex emptiness. Note that containment is based on ID equality,
1254/// so elements must have been added to the complex to be considered contained.
1255impl<T: ComplexElement> Collection for Complex<T> {
1256 type Item = T;
1257
1258 fn contains(&self, point: &Self::Item) -> bool {
1259 if let Some(id) = point.id() {
1260 self.elements.contains_key(&id)
1261 } else {
1262 false
1263 }
1264 }
1265
1266 fn is_empty(&self) -> bool { self.elements.is_empty() }
1267}
1268
1269/// Implementation of [`Poset`] for complexes.
1270///
1271/// Defines the face relation as the partial order: σ ≤ τ means "σ is a face of τ".
1272/// This implementation delegates to the attachment lattice for efficiency while
1273/// providing the full element objects in the interface.
1274///
1275/// The face relation satisfies all poset axioms:
1276/// - **Reflexivity**: σ ≤ σ (every element is a face of itself)
1277/// - **Antisymmetry**: σ ≤ τ and τ ≤ σ implies σ = τ
1278/// - **Transitivity**: σ ≤ τ and τ ≤ ρ implies σ ≤ ρ
1279impl<T: ComplexElement> Poset for Complex<T> {
1280 fn leq(&self, a: &Self::Item, b: &Self::Item) -> Option<bool> {
1281 match (a.id(), b.id()) {
1282 (Some(id_a), Some(id_b)) => self.attachment_lattice.leq(&id_a, &id_b),
1283 _ => None,
1284 }
1285 }
1286
1287 fn upset(&self, a: Self::Item) -> std::collections::HashSet<Self::Item> {
1288 if let Some(id) = a.id() {
1289 self
1290 .attachment_lattice
1291 .upset(id)
1292 .into_iter()
1293 .filter_map(|upset_id| self.get_element(upset_id).cloned())
1294 .collect()
1295 } else {
1296 std::collections::HashSet::new()
1297 }
1298 }
1299
1300 fn downset(&self, a: Self::Item) -> std::collections::HashSet<Self::Item> {
1301 if let Some(id) = a.id() {
1302 self
1303 .attachment_lattice
1304 .downset(id)
1305 .into_iter()
1306 .filter_map(|downset_id| self.get_element(downset_id).cloned())
1307 .collect()
1308 } else {
1309 std::collections::HashSet::new()
1310 }
1311 }
1312
1313 fn minimal_elements(&self) -> std::collections::HashSet<Self::Item> {
1314 self
1315 .attachment_lattice
1316 .minimal_elements()
1317 .into_iter()
1318 .filter_map(|id| self.get_element(id).cloned())
1319 .collect()
1320 }
1321
1322 fn maximal_elements(&self) -> std::collections::HashSet<Self::Item> {
1323 self
1324 .attachment_lattice
1325 .maximal_elements()
1326 .into_iter()
1327 .filter_map(|id| self.get_element(id).cloned())
1328 .collect()
1329 }
1330
1331 fn join(&self, a: Self::Item, b: Self::Item) -> Option<Self::Item> {
1332 match (a.id(), b.id()) {
1333 (Some(id_a), Some(id_b)) => self
1334 .attachment_lattice
1335 .join(id_a, id_b)
1336 .and_then(|join_id| self.get_element(join_id).cloned()),
1337 _ => None,
1338 }
1339 }
1340
1341 fn meet(&self, a: Self::Item, b: Self::Item) -> Option<Self::Item> {
1342 match (a.id(), b.id()) {
1343 (Some(id_a), Some(id_b)) => self
1344 .attachment_lattice
1345 .meet(id_a, id_b)
1346 .and_then(|meet_id| self.get_element(meet_id).cloned()),
1347 _ => None,
1348 }
1349 }
1350
1351 fn successors(&self, a: Self::Item) -> std::collections::HashSet<Self::Item> {
1352 if let Some(id) = a.id() {
1353 self
1354 .attachment_lattice
1355 .successors(id)
1356 .into_iter()
1357 .filter_map(|succ_id| self.get_element(succ_id).cloned())
1358 .collect()
1359 } else {
1360 std::collections::HashSet::new()
1361 }
1362 }
1363
1364 fn predecessors(&self, a: Self::Item) -> std::collections::HashSet<Self::Item> {
1365 if let Some(id) = a.id() {
1366 self
1367 .attachment_lattice
1368 .predecessors(id)
1369 .into_iter()
1370 .filter_map(|pred_id| self.get_element(pred_id).cloned())
1371 .collect()
1372 } else {
1373 std::collections::HashSet::new()
1374 }
1375 }
1376}
1377
1378/// Implementation of [`Topology`] for complexes.
1379///
1380/// Provides topological operations that integrate with the broader framework:
1381/// - **Neighborhood**: Returns cofaces (elements containing the given element)
1382/// - **Boundary**: Computes oriented boundary using element-specific operators
1383///
1384/// The boundary implementation is crucial for homology computations as it provides
1385/// the chain complex structure with proper orientation handling.
1386impl<T: ComplexElement> Topology for Complex<T> {
1387 fn neighborhood(&self, item: &Self::Item) -> Vec<Self::Item> {
1388 // Return direct cofaces (elements that have this item as a face)
1389 self.cofaces(item)
1390 }
1391
1392 fn boundary<R: Ring + Copy>(&self, item: &Self::Item) -> Chain<Self, R> {
1393 if item.dimension() == 0 {
1394 return Chain::new(self);
1395 }
1396
1397 let mut boundary_chain_items = Vec::new();
1398 let mut boundary_chain_coeffs = Vec::new();
1399
1400 // Use the element-specific boundary computation with orientations
1401 let faces_with_orientations = item.boundary_with_orientations();
1402
1403 for (face, orientation) in faces_with_orientations {
1404 // Find the corresponding element in the complex that matches this face's content
1405 if let Some(complex_face) = self.find_equivalent_element(&face) {
1406 // Use the orientation coefficient from the element-specific boundary operator
1407 let coeff = if orientation > 0 {
1408 R::one()
1409 } else if orientation < 0 {
1410 -R::one()
1411 } else {
1412 continue; // Skip faces with zero coefficient
1413 };
1414 boundary_chain_items.push(complex_face);
1415 boundary_chain_coeffs.push(coeff);
1416 }
1417 }
1418
1419 Chain::from_items_and_coeffs(self, boundary_chain_items, boundary_chain_coeffs)
1420 }
1421}
1422
1423#[cfg(test)]
1424mod tests {
1425 use harness_algebra::algebras::boolean::Boolean;
1426
1427 use super::*;
1428
1429 // =============================================================================
1430 // COMPREHENSIVE TEST SUITE
1431 // =============================================================================
1432 //
1433 // This test suite demonstrates and validates the key features of the complexes
1434 // module across multiple dimensions:
1435 //
1436 // 1. **Generic Complex Operations**: Tests that work with both simplicial and cubical complexes,
1437 // showing the power of the generic Complex<T> design
1438 //
1439 // 2. **Closure Property**: Verifies that adding elements automatically includes all faces,
1440 // maintaining the fundamental complex property
1441 //
1442 // 3. **ID Management**: Tests automatic ID assignment, deduplication, and proper handling of
1443 // elements with/without IDs
1444 //
1445 // 4. **Poset Structure**: Validates that face relations form a proper partial order with correct
1446 // upset/downset computations
1447 //
1448 // 5. **Topology Integration**: Tests neighborhood operations and boundary computations that
1449 // integrate with the topology framework
1450 //
1451 // 6. **Homology Computations**: Comprehensive tests of homology computation for standard
1452 // topological spaces (circles, spheres, etc.) over different coefficient fields
1453 //
1454 // 7. **Cross-Complex Compatibility**: Demonstrates that the same algorithms work on both
1455 // simplicial and cubical complexes, producing mathematically consistent results
1456 //
1457 // These tests serve both as validation and as examples of proper usage patterns.
1458
1459 #[test]
1460 fn test_generic_complex_with_simplex() {
1461 let mut complex: Complex<Simplex> = Complex::new();
1462
1463 // Create a triangle (2-simplex)
1464 let triangle = Simplex::new(2, vec![0, 1, 2]);
1465 let added_triangle = complex.join_element(triangle);
1466
1467 // Should have 1 triangle, 3 edges, 3 vertices
1468 assert_eq!(complex.elements_of_dimension(2).len(), 1);
1469 assert_eq!(complex.elements_of_dimension(1).len(), 3);
1470 assert_eq!(complex.elements_of_dimension(0).len(), 3);
1471
1472 // Check that the triangle is in the complex (use the returned element with ID)
1473 assert!(complex.contains(&added_triangle));
1474
1475 // Check lattice relationships
1476 let faces = complex.faces(&added_triangle);
1477 assert_eq!(faces.len(), 3); // Triangle should have 3 edges as direct faces
1478 }
1479
1480 #[test]
1481 fn test_generic_complex_with_cube() {
1482 let mut complex: Complex<Cube> = Complex::new();
1483
1484 // Create a square (2-cube)
1485 let square = Cube::square([0, 1, 2, 3]);
1486 let added_square = complex.join_element(square);
1487
1488 // Should have 1 square, 4 edges, 4 vertices
1489 assert_eq!(complex.elements_of_dimension(2).len(), 1);
1490 assert_eq!(complex.elements_of_dimension(1).len(), 4);
1491 assert_eq!(complex.elements_of_dimension(0).len(), 4);
1492
1493 // Check that the square is in the complex (use the returned element with ID)
1494 assert!(complex.contains(&added_square));
1495
1496 // Check lattice relationships
1497 let faces = complex.faces(&added_square);
1498 assert_eq!(faces.len(), 4); // Square should have 4 edges as direct faces
1499 }
1500
1501 #[test]
1502 fn test_automatic_id_assignment() {
1503 let mut complex = SimplicialComplex::new();
1504
1505 // Create elements without IDs
1506 let vertex1 = Simplex::new(0, vec![0]);
1507 let vertex2 = Simplex::new(0, vec![1]);
1508 let edge = Simplex::new(1, vec![0, 1]);
1509
1510 // Add them to the complex - should get automatic ID assignment
1511 let added_vertex1 = complex.join_element(vertex1);
1512 let added_vertex2 = complex.join_element(vertex2);
1513 let added_edge = complex.join_element(edge);
1514
1515 // All should be contained
1516 assert!(complex.contains(&added_vertex1));
1517 assert!(complex.contains(&added_vertex2));
1518 assert!(complex.contains(&added_edge));
1519
1520 // Should have assigned IDs
1521 assert!(added_vertex1.id().is_some());
1522 assert!(added_vertex2.id().is_some());
1523 assert!(added_edge.id().is_some());
1524
1525 // Should have proper lattice relationships
1526 assert!(complex.leq(&added_vertex1, &added_edge).unwrap());
1527 assert!(complex.leq(&added_vertex2, &added_edge).unwrap());
1528 }
1529
1530 #[test]
1531 fn test_element_deduplication() {
1532 let mut complex = SimplicialComplex::new();
1533
1534 // Create two mathematically identical simplices
1535 let simplex1 = Simplex::new(1, vec![0, 1]);
1536 let simplex2 = Simplex::new(1, vec![0, 1]); // Same content
1537
1538 let added1 = complex.join_element(simplex1);
1539 let added2 = complex.join_element(simplex2);
1540
1541 // Should return the same element (by ID)
1542 assert_eq!(added1.id(), added2.id());
1543 assert_eq!(complex.elements_of_dimension(1).len(), 1); // Only one edge stored
1544 }
1545
1546 // =====================================================
1547 // GENERIC POSET OPERATIONS TESTS
1548 // =====================================================
1549
1550 #[test]
1551 fn test_complex_poset_operations() {
1552 let mut complex = SimplicialComplex::new();
1553
1554 let triangle = Simplex::new(2, vec![0, 1, 2]);
1555 let added_triangle = complex.join_element(triangle);
1556
1557 // Get the actual elements with IDs from the complex
1558 let vertices = complex.elements_of_dimension(0);
1559 let edges = complex.elements_of_dimension(1);
1560
1561 // Find specific vertex and edge by content
1562 let vertex = vertices.iter().find(|v| v.vertices() == [0]).unwrap().clone();
1563 let edge = edges.iter().find(|e| e.vertices() == [0, 1]).unwrap().clone();
1564
1565 // Test leq relationships
1566 assert_eq!(complex.leq(&vertex, &edge), Some(true));
1567 assert_eq!(complex.leq(&edge, &added_triangle), Some(true));
1568 assert_eq!(complex.leq(&vertex, &added_triangle), Some(true));
1569 assert_eq!(complex.leq(&added_triangle, &vertex), Some(false));
1570
1571 // Test upset/downset
1572 let vertex_upset = complex.upset(vertex.clone());
1573 assert!(vertex_upset.contains(&vertex));
1574 assert!(vertex_upset.contains(&edge));
1575 assert!(vertex_upset.contains(&added_triangle));
1576
1577 let triangle_downset = complex.downset(added_triangle.clone());
1578 assert!(triangle_downset.contains(&vertex));
1579 assert!(triangle_downset.contains(&edge));
1580 assert!(triangle_downset.contains(&added_triangle));
1581 }
1582
1583 #[test]
1584 fn test_complex_topology_operations() {
1585 let mut complex = SimplicialComplex::new();
1586
1587 let edge = Simplex::new(1, vec![0, 1]);
1588 let added_edge = complex.join_element(edge);
1589
1590 // Get the actual vertex with ID from the complex
1591 let vertices = complex.elements_of_dimension(0);
1592 let vertex = vertices.iter().find(|v| v.vertices() == [0]).unwrap().clone();
1593
1594 // Test neighborhood (cofaces)
1595 let vertex_neighborhood = complex.neighborhood(&vertex);
1596 assert_eq!(vertex_neighborhood.len(), 1);
1597 assert!(vertex_neighborhood.contains(&added_edge));
1598
1599 let edge_neighborhood = complex.neighborhood(&added_edge);
1600 assert_eq!(edge_neighborhood.len(), 0); // No 2-simplices attached
1601 }
1602
1603 #[test]
1604 fn test_mixed_complex_operations() {
1605 // Test that we can use the same generic operations on both simplicial and cubical complexes
1606
1607 // Simplicial complex
1608 let mut simplicial_complex = SimplicialComplex::new();
1609 let triangle = Simplex::from_vertices(vec![0, 1, 2]);
1610 let added_triangle = simplicial_complex.join_element(triangle);
1611
1612 // Should automatically add all faces
1613 assert_eq!(simplicial_complex.elements_of_dimension(2).len(), 1);
1614 assert_eq!(simplicial_complex.elements_of_dimension(1).len(), 3);
1615 assert_eq!(simplicial_complex.elements_of_dimension(0).len(), 3);
1616
1617 // Cubical complex
1618 let mut cubical_complex = CubicalComplex::new();
1619 let square = Cube::square([0, 1, 2, 3]);
1620 let added_square = cubical_complex.join_element(square);
1621
1622 // Should automatically add all faces
1623 assert_eq!(cubical_complex.elements_of_dimension(2).len(), 1);
1624 assert_eq!(cubical_complex.elements_of_dimension(1).len(), 4);
1625 assert_eq!(cubical_complex.elements_of_dimension(0).len(), 4);
1626
1627 // Both should support the same interface
1628 assert!(simplicial_complex.contains(&added_triangle));
1629 assert!(cubical_complex.contains(&added_square));
1630
1631 // Both should have proper lattice structure
1632 let triangle_faces = simplicial_complex.faces(&added_triangle);
1633 let square_faces = cubical_complex.faces(&added_square);
1634 assert_eq!(triangle_faces.len(), 3); // triangle has 3 edges
1635 assert_eq!(square_faces.len(), 4); // square has 4 edges
1636 }
1637
1638 #[test]
1639 fn test_generic_homology_computation() {
1640 // Test simplicial complex - triangle boundary (should have H_1 = 1)
1641 let mut simplicial_complex = SimplicialComplex::new();
1642
1643 // Create a triangle boundary (3 edges forming a cycle)
1644 let edge1 = Simplex::new(1, vec![0, 1]);
1645 let edge2 = Simplex::new(1, vec![1, 2]);
1646 let edge3 = Simplex::new(1, vec![2, 0]);
1647
1648 simplicial_complex.join_element(edge1);
1649 simplicial_complex.join_element(edge2);
1650 simplicial_complex.join_element(edge3);
1651
1652 // Compute homology over Z/2Z
1653 let h0 = simplicial_complex.homology::<Boolean>(0);
1654 let h1 = simplicial_complex.homology::<Boolean>(1);
1655
1656 assert_eq!(h0.betti_number, 1); // One connected component
1657 assert_eq!(h1.betti_number, 1); // One 1-dimensional hole
1658
1659 // Test cubical complex - square boundary (should also have H_1 = 1)
1660 let mut cubical_complex = CubicalComplex::new();
1661
1662 // Create a square boundary (4 edges forming a cycle)
1663 let edge1 = Cube::edge(0, 1);
1664 let edge2 = Cube::edge(1, 2);
1665 let edge3 = Cube::edge(2, 3);
1666 let edge4 = Cube::edge(3, 0);
1667
1668 cubical_complex.join_element(edge1);
1669 cubical_complex.join_element(edge2);
1670 cubical_complex.join_element(edge3);
1671 cubical_complex.join_element(edge4);
1672
1673 // Compute homology over Z/2Z
1674 let h0_cube = cubical_complex.homology::<Boolean>(0);
1675 let h1_cube = cubical_complex.homology::<Boolean>(1);
1676
1677 assert_eq!(h0_cube.betti_number, 1); // One connected component
1678 assert_eq!(h1_cube.betti_number, 1); // One 1-dimensional hole
1679
1680 // Both simplicial and cubical complexes should give same topological result
1681 assert_eq!(h0.betti_number, h0_cube.betti_number);
1682 assert_eq!(h1.betti_number, h1_cube.betti_number);
1683 }
1684
1685 #[test]
1686 fn test_generic_homology_filled_shapes() {
1687 // Test filled triangle (should have H_1 = 0)
1688 let mut simplicial_complex = SimplicialComplex::new();
1689 let triangle = Simplex::new(2, vec![0, 1, 2]);
1690 simplicial_complex.join_element(triangle);
1691
1692 let h0 = simplicial_complex.homology::<Boolean>(0);
1693 let h1 = simplicial_complex.homology::<Boolean>(1);
1694
1695 assert_eq!(h0.betti_number, 1); // One connected component
1696 assert_eq!(h1.betti_number, 0); // No 1D holes (filled)
1697
1698 // Test filled square (should also have H_1 = 0)
1699 let mut cubical_complex = CubicalComplex::new();
1700 let square = Cube::square([0, 1, 2, 3]);
1701 cubical_complex.join_element(square);
1702
1703 let h0_cube = cubical_complex.homology::<Boolean>(0);
1704 let h1_cube = cubical_complex.homology::<Boolean>(1);
1705
1706 assert_eq!(h0_cube.betti_number, 1); // One connected component
1707 assert_eq!(h1_cube.betti_number, 0); // No 1D holes (filled)
1708 }
1709}