#![allow(non_snake_case)]
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct EmergenceResult {
pub h0: usize,
pub h1: usize,
pub emergence_detected: bool,
pub n_edges: usize,
pub n_vertices: usize,
pub confidence: f64,
}
impl EmergenceResult {
pub fn has_emergence(&self) -> bool {
let rigidity_threshold = if self.n_vertices >= 3 { 2 * self.n_vertices - 3 } else { 0 };
self.n_edges > rigidity_threshold
}
}
#[derive(Clone)]
pub struct EmergenceDetector;
impl EmergenceDetector {
pub fn preliminary_screen(V: usize, E: usize) -> bool {
if V == 0 || E == 0 {
return false; }
if E < V.saturating_sub(1) {
return false;
}
let max_edges = V * (V - 1) / 2;
if E > max_edges / 4 {
return false; }
let lamanc_threshold = if V >= 3 { 2 * V - 3 } else { 0 };
let lower = (lamanc_threshold as f64 * 0.9) as usize;
let upper = (lamanc_threshold as f64 * 1.1) as usize;
if E >= lower && E <= upper {
return false; }
true
}
pub fn detect(n_vertices: usize, n_edges: usize, n_components: usize) -> EmergenceResult {
let h0 = n_components;
let h1 = if n_edges >= n_vertices {
n_edges - n_vertices + n_components
} else {
0
};
let threshold = if n_vertices >= 3 { 2 * n_vertices - 3 } else { 0 };
let detected = n_edges > threshold;
let threshold = if n_vertices >= 3 { 2 * n_vertices - 3 } else { 0 };
let connectivity = if n_vertices > 0 {
(n_edges as f64 - threshold as f64) / (n_vertices as f64 * 2.0)
} else {
0.0
};
let confidence = 0.5 + connectivity.clamp(0.0, 0.5);
EmergenceResult {
h0,
h1,
emergence_detected: detected,
n_edges,
n_vertices,
confidence,
}
}
pub fn detect_graph<V: Into<usize>, E: Into<usize>>(V: V, E: E) -> EmergenceResult {
Self::detect(V.into(), E.into(), 1)
}
pub fn detect_beta(n_vertices: usize, n_edges: usize) -> EmergenceResult {
Self::detect(n_vertices, n_edges, 1)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_no_emergence_isolated_nodes() {
let result = EmergenceDetector::detect(1024, 0, 1);
assert!(!result.emergence_detected);
}
#[test]
fn test_emergence_in_complete_graph() {
let result = EmergenceDetector::detect(12, 66, 1);
assert!(result.emergence_detected);
}
#[test]
fn test_emergence_threshold() {
let result = EmergenceDetector::detect(12, 21, 1);
assert!(!result.emergence_detected);
let result = EmergenceDetector::detect(12, 22, 1);
assert!(result.emergence_detected);
}
#[test]
fn test_laman_rigid_boundary() {
for V in [3, 5, 10, 50] {
let E = 2 * V - 3;
let result = EmergenceDetector::detect(V, E, 1);
assert!(!result.emergence_detected, "V={V} E={E} should be exactly rigid, no emergence");
}
}
#[test]
fn test_fleet_scenario() {
let result = EmergenceDetector::detect(1024, 12000, 1);
assert!(result.emergence_detected);
assert!(result.confidence > 0.9);
}
#[test]
fn test_beta_coefficient() {
let result = EmergenceDetector::detect(10, 10, 1);
assert_eq!(result.h1, 1); }
#[test]
fn test_preliminary_screen_empty() {
assert!(!EmergenceDetector::preliminary_screen(0, 0));
assert!(!EmergenceDetector::preliminary_screen(100, 0));
assert!(!EmergenceDetector::preliminary_screen(100, 0));
}
#[test]
fn test_preliminary_screen_disconnected() {
assert!(!EmergenceDetector::preliminary_screen(10, 5)); assert!(!EmergenceDetector::preliminary_screen(100, 50)); }
#[test]
fn test_preliminary_screen_massively_overconnected() {
assert!(!EmergenceDetector::preliminary_screen(10, 30)); }
#[test]
fn test_preliminary_screen_laman_rigid_zone() {
assert!(!EmergenceDetector::preliminary_screen(10, 17)); assert!(!EmergenceDetector::preliminary_screen(10, 15)); assert!(!EmergenceDetector::preliminary_screen(10, 19)); }
#[test]
fn test_preliminary_screen_needs_h1() {
assert!(EmergenceDetector::preliminary_screen(20, 44));
assert!(EmergenceDetector::preliminary_screen(50, 200)); }
}