use crate::core::FactorizationStateSpace;
use crate::topology::{FactorizationSimplex, PPFGaloisGroup};
use crate::algebra::homology::HomologyGroups;
use std::collections::{HashMap, HashSet};
use std::fmt;
use thiserror::Error;
use serde::{Deserialize, Serialize};
#[derive(Error, Debug, Clone, PartialEq)]
pub enum BettiError {
#[error("Invalid dimension: {0}")]
InvalidDimension(i32),
#[error("Computation error: {0}")]
ComputationError(String),
#[error("Insufficient data: {0}")]
InsufficientData(String),
}
#[derive(Debug, Clone)]
pub struct BettiNumberComputer {
state_space: FactorizationStateSpace,
simplex: FactorizationSimplex,
galois_group: PPFGaloisGroup,
betti_cache: HashMap<i32, usize>,
torsion_cache: HashMap<i32, Vec<usize>>,
}
impl BettiNumberComputer {
pub fn new(state_space: FactorizationStateSpace) -> Self {
let simplex = FactorizationSimplex::new(state_space.clone());
let galois_group = PPFGaloisGroup::new(state_space.clone());
BettiNumberComputer {
state_space,
simplex,
galois_group,
betti_cache: HashMap::new(),
torsion_cache: HashMap::new(),
}
}
pub fn betti_number(&mut self, k: i32) -> Result<usize, BettiError> {
if k < 0 {
return Err(BettiError::InvalidDimension(k));
}
if let Some(&cached) = self.betti_cache.get(&k) {
return Ok(cached);
}
let result = self.compute_betti_number(k)?;
self.betti_cache.insert(k, result);
Ok(result)
}
fn compute_betti_number(&self, k: i32) -> Result<usize, BettiError> {
match k {
0 => Ok(self.compute_b0()),
1 => Ok(self.compute_b1()),
_ => self.compute_higher_betti(k),
}
}
fn compute_b0(&self) -> usize {
if self.simplex.is_connected() {
1
} else {
self.count_connected_components()
}
}
fn count_connected_components(&self) -> usize {
let vertices = self.simplex.vertices();
let mut visited = HashSet::new();
let mut components = 0;
for i in 0..vertices.len() {
if !visited.contains(&i) {
self.dfs_component(i, &mut visited);
components += 1;
}
}
components
}
fn dfs_component(&self, start: usize, visited: &mut HashSet<usize>) {
visited.insert(start);
for edge in self.simplex.edges() {
let neighbor = if edge.source == start {
Some(edge.target)
} else if edge.target == start {
Some(edge.source)
} else {
None
};
if let Some(next) = neighbor {
if !visited.contains(&next) {
self.dfs_component(next, visited);
}
}
}
}
fn compute_b1(&self) -> usize {
let distinct_primes = self.count_distinct_primes();
distinct_primes.saturating_sub(1)
}
fn count_distinct_primes(&self) -> usize {
let mut primes = HashSet::new();
if let Some(factorization) = self.state_space.factorizations().first() {
for (&prime, _) in factorization.factors() {
if prime != -1 { primes.insert(prime.abs());
}
}
}
primes.len()
}
fn compute_higher_betti(&self, k: i32) -> Result<usize, BettiError> {
let k_usize = k as usize;
let num_primes = self.count_distinct_primes();
if num_primes == 0 {
return Ok(0);
}
let torus_dim = num_primes.saturating_sub(1);
if k_usize > torus_dim {
Ok(0)
} else {
Ok(binomial_coefficient(torus_dim, k_usize))
}
}
pub fn betti_numbers(&mut self, max_dim: i32) -> Result<Vec<usize>, BettiError> {
let mut numbers = Vec::new();
for k in 0..=max_dim {
numbers.push(self.betti_number(k)?);
}
Ok(numbers)
}
pub fn euler_characteristic(&mut self) -> Result<i32, BettiError> {
let max_dim = self.simplex.dimension() as i32;
let betti_numbers = self.betti_numbers(max_dim)?;
let chi = betti_numbers
.iter()
.enumerate()
.map(|(i, &b)| {
let sign = if i % 2 == 0 { 1 } else { -1 };
sign * b as i32
})
.sum();
Ok(chi)
}
pub fn poincare_polynomial(&mut self, max_dim: i32) -> Result<PoincarePolynomial, BettiError> {
let betti_numbers = self.betti_numbers(max_dim)?;
Ok(PoincarePolynomial::new(betti_numbers))
}
pub fn topological_type(&mut self) -> Result<TopologicalType, BettiError> {
let num_primes = self.count_distinct_primes();
let b1 = self.betti_number(1)?;
let chi = self.euler_characteristic()?;
if chi == 0 && num_primes > 0 {
TopologicalType::determine_torus_type(num_primes, b1)
} else if chi == 2 {
Ok(TopologicalType::Sphere)
} else if chi == 1 {
Ok(TopologicalType::Disk)
} else {
Ok(TopologicalType::Unknown)
}
}
pub fn homology_groups(&mut self, max_dim: i32) -> Result<HomologyGroups, BettiError> {
let mut groups = HomologyGroups::new();
for k in 0..=max_dim {
let betti = self.betti_number(k)?;
groups.set_betti_number(k, betti);
}
Ok(groups)
}
pub fn persistence_betti_numbers(&self, filtration_values: &[f64]) -> Result<Vec<Vec<usize>>, BettiError> {
let mut persistence = Vec::new();
for &value in filtration_values {
let filtered_complex = self.filter_complex(value)?;
let betti = self.compute_betti_for_complex(&filtered_complex)?;
persistence.push(betti);
}
Ok(persistence)
}
fn filter_complex(&self, _value: f64) -> Result<FilteredComplex, BettiError> {
Ok(FilteredComplex {
vertices: self.simplex.vertices().len(),
edges: self.simplex.edges().len(),
dimension: self.simplex.dimension(),
})
}
fn compute_betti_for_complex(&self, complex: &FilteredComplex) -> Result<Vec<usize>, BettiError> {
let mut betti = Vec::new();
betti.push(1);
if complex.dimension >= 1 {
let b1 = self.count_distinct_primes().saturating_sub(1);
betti.push(b1);
}
for k in 2..=complex.dimension {
let torus_dim = self.count_distinct_primes().saturating_sub(1);
if k <= torus_dim {
betti.push(binomial_coefficient(torus_dim, k));
} else {
betti.push(0);
}
}
Ok(betti)
}
pub fn spectral_sequence(&self) -> Result<SpectralSequence, BettiError> {
let mut e2_page = HashMap::new();
let num_primes = self.count_distinct_primes();
for p in 0..=num_primes {
for q in 0..=num_primes {
let rank = if p + q <= num_primes {
binomial_coefficient(num_primes, p) * binomial_coefficient(num_primes, q)
} else {
0
};
e2_page.insert((p, q), rank);
}
}
Ok(SpectralSequence::new(e2_page))
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum TopologicalType {
Torus(usize),
Sphere,
Disk,
TorusProduct(Vec<usize>),
Unknown,
}
impl TopologicalType {
fn determine_torus_type(num_primes: usize, b1: usize) -> Result<TopologicalType, BettiError> {
if b1 == num_primes.saturating_sub(1) {
Ok(TopologicalType::Torus(b1))
} else {
Ok(TopologicalType::Unknown)
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PoincarePolynomial {
coefficients: Vec<usize>,
}
impl PoincarePolynomial {
pub fn new(betti_numbers: Vec<usize>) -> Self {
PoincarePolynomial {
coefficients: betti_numbers,
}
}
pub fn evaluate(&self, t: f64) -> f64 {
self.coefficients
.iter()
.enumerate()
.map(|(i, &coeff)| coeff as f64 * t.powi(i as i32))
.sum()
}
pub fn degree(&self) -> usize {
self.coefficients.len().saturating_sub(1)
}
}
#[derive(Debug, Clone)]
struct FilteredComplex {
vertices: usize,
edges: usize,
dimension: usize,
}
#[derive(Debug, Clone)]
pub struct SpectralSequence {
e2_page: HashMap<(usize, usize), usize>,
}
impl SpectralSequence {
fn new(e2_page: HashMap<(usize, usize), usize>) -> Self {
SpectralSequence { e2_page }
}
pub fn e2_rank(&self, p: usize, q: usize) -> usize {
self.e2_page.get(&(p, q)).copied().unwrap_or(0)
}
pub fn converges(&self) -> bool {
true
}
}
fn binomial_coefficient(n: usize, k: usize) -> usize {
if k > n {
0
} else if k == 0 || k == n {
1
} else {
let k = k.min(n - k);
(1..=k).fold(1, |acc, i| acc * (n - i + 1) / i)
}
}
impl fmt::Display for TopologicalType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TopologicalType::Torus(k) => write!(f, "T^{} ({}D torus)", k, k),
TopologicalType::Sphere => write!(f, "S^n (sphere)"),
TopologicalType::Disk => write!(f, "D^n (disk)"),
TopologicalType::TorusProduct(dims) => {
write!(f, "T^{} × T^{} (torus product)", dims[0], dims[1])
}
TopologicalType::Unknown => write!(f, "Unknown"),
}
}
}
impl fmt::Display for PoincarePolynomial {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "P(t) = ")?;
for (i, &coeff) in self.coefficients.iter().enumerate() {
if i > 0 && coeff > 0 {
write!(f, " + ")?;
}
if coeff > 0 {
if i == 0 {
write!(f, "{}", coeff)?;
} else if i == 1 {
write!(f, "{}t", coeff)?;
} else {
write!(f, "{}t^{}", coeff, i)?;
}
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_betti_number_computation() {
let state_space = FactorizationStateSpace::new(6).unwrap(); let mut computer = BettiNumberComputer::new(state_space);
assert_eq!(computer.betti_number(0).unwrap(), 2); assert_eq!(computer.betti_number(1).unwrap(), 1); assert_eq!(computer.betti_number(2).unwrap(), 0); }
#[test]
fn test_euler_characteristic() {
let state_space = FactorizationStateSpace::new(6).unwrap();
let mut computer = BettiNumberComputer::new(state_space);
assert_eq!(computer.euler_characteristic().unwrap(), 1);
}
#[test]
fn test_topological_type() {
let state_space = FactorizationStateSpace::new(6).unwrap();
let mut computer = BettiNumberComputer::new(state_space);
let topo_type = computer.topological_type().unwrap();
match topo_type {
TopologicalType::Disk => {
},
TopologicalType::Torus(k) => assert_eq!(k, 1),
_ => panic!("Unexpected topological type: {:?}", topo_type),
}
}
#[test]
fn test_poincare_polynomial() {
let state_space = FactorizationStateSpace::new(30).unwrap(); let mut computer = BettiNumberComputer::new(state_space);
let poly = computer.poincare_polynomial(3).unwrap();
let sum = poly.evaluate(1.0);
assert!(sum > 0.0);
assert!(poly.degree() >= 1);
}
#[test]
fn test_quantum_vs_classical() {
let classical = FactorizationStateSpace::new(6).unwrap();
let mut classical_computer = BettiNumberComputer::new(classical);
let quantum = FactorizationStateSpace::new(-6).unwrap();
let mut quantum_computer = BettiNumberComputer::new(quantum);
assert_eq!(
classical_computer.betti_number(1).unwrap(),
quantum_computer.betti_number(1).unwrap()
);
let classical_chi = classical_computer.euler_characteristic().unwrap();
let quantum_chi = quantum_computer.euler_characteristic().unwrap();
assert!(classical_chi > 0);
assert!(quantum_chi > 0);
}
#[test]
fn test_spectral_sequence() {
let state_space = FactorizationStateSpace::new(6).unwrap();
let computer = BettiNumberComputer::new(state_space);
let ss = computer.spectral_sequence().unwrap();
assert!(ss.converges());
assert!(ss.e2_rank(0, 0) > 0);
}
#[test]
fn test_persistence() {
let state_space = FactorizationStateSpace::new(12).unwrap();
let computer = BettiNumberComputer::new(state_space);
let filtration = vec![0.0, 0.5, 1.0];
let persistence = computer.persistence_betti_numbers(&filtration).unwrap();
assert_eq!(persistence.len(), 3);
for betti_vec in &persistence {
assert!(!betti_vec.is_empty());
}
}
#[test]
fn test_binomial_coefficient() {
assert_eq!(binomial_coefficient(4, 0), 1);
assert_eq!(binomial_coefficient(4, 1), 4);
assert_eq!(binomial_coefficient(4, 2), 6);
assert_eq!(binomial_coefficient(4, 3), 4);
assert_eq!(binomial_coefficient(4, 4), 1);
assert_eq!(binomial_coefficient(4, 5), 0);
}
#[test]
fn test_display() {
let torus = TopologicalType::Torus(2);
assert_eq!(format!("{}", torus), "T^2 (2D torus)");
let poly = PoincarePolynomial::new(vec![1, 2, 1]);
let poly_str = format!("{}", poly);
assert!(poly_str.contains("P(t)"));
assert!(poly_str.contains("t"));
}
}