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}