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