use super::{EdgeId, VertexId, Weight};
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IsolationResult {
pub isolated_vertices: HashSet<VertexId>,
pub cut_edges: Vec<EdgeId>,
pub cut_value: f64,
pub num_high_energy_edges: usize,
pub threshold: Weight,
pub is_verified: bool,
}
impl IsolationResult {
pub fn no_isolation() -> Self {
Self {
isolated_vertices: HashSet::new(),
cut_edges: vec![],
cut_value: 0.0,
num_high_energy_edges: 0,
threshold: 0.0,
is_verified: true,
}
}
pub fn has_isolation(&self) -> bool {
!self.isolated_vertices.is_empty()
}
pub fn num_isolated(&self) -> usize {
self.isolated_vertices.len()
}
pub fn num_cut_edges(&self) -> usize {
self.cut_edges.len()
}
pub fn efficiency(&self) -> f64 {
if self.isolated_vertices.is_empty() {
return 0.0;
}
self.cut_value / self.isolated_vertices.len() as f64
}
pub fn is_isolated(&self, vertex: VertexId) -> bool {
self.isolated_vertices.contains(&vertex)
}
pub fn boundary_vertices(&self) -> HashSet<VertexId> {
let mut boundary = HashSet::new();
for (u, v) in &self.cut_edges {
if self.isolated_vertices.contains(u) {
boundary.insert(*u);
}
if self.isolated_vertices.contains(v) {
boundary.insert(*v);
}
}
boundary
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IsolationRegion {
pub vertices: HashSet<VertexId>,
pub internal_edges: Vec<EdgeId>,
pub boundary_edges: Vec<EdgeId>,
pub total_energy: Weight,
pub boundary_weight: Weight,
pub region_id: usize,
}
impl IsolationRegion {
pub fn num_vertices(&self) -> usize {
self.vertices.len()
}
pub fn num_internal_edges(&self) -> usize {
self.internal_edges.len()
}
pub fn num_boundary_edges(&self) -> usize {
self.boundary_edges.len()
}
pub fn density(&self) -> f64 {
if self.vertices.is_empty() {
return 0.0;
}
self.internal_edges.len() as f64 / self.vertices.len() as f64
}
pub fn boundary_ratio(&self) -> f64 {
if self.internal_edges.is_empty() {
return f64::INFINITY;
}
self.boundary_edges.len() as f64 / self.internal_edges.len() as f64
}
pub fn avg_energy(&self) -> Weight {
if self.internal_edges.is_empty() {
return 0.0;
}
self.total_energy / self.internal_edges.len() as f64
}
pub fn contains(&self, vertex: VertexId) -> bool {
self.vertices.contains(&vertex)
}
pub fn is_internal_edge(&self, edge: &EdgeId) -> bool {
self.internal_edges.contains(edge)
}
pub fn is_boundary_edge(&self, edge: &EdgeId) -> bool {
self.boundary_edges.contains(edge)
}
pub fn boundary_vertices(&self) -> HashSet<VertexId> {
let mut boundary = HashSet::new();
for (u, v) in &self.boundary_edges {
if self.vertices.contains(u) {
boundary.insert(*u);
}
if self.vertices.contains(v) {
boundary.insert(*v);
}
}
boundary
}
pub fn interior_vertices(&self) -> HashSet<VertexId> {
let boundary = self.boundary_vertices();
self.vertices
.iter()
.filter(|v| !boundary.contains(v))
.copied()
.collect()
}
}
#[derive(Debug, Clone)]
pub struct IsolationComparison {
pub common_isolated: HashSet<VertexId>,
pub only_first: HashSet<VertexId>,
pub only_second: HashSet<VertexId>,
pub jaccard_similarity: f64,
}
impl IsolationComparison {
pub fn compare(first: &IsolationResult, second: &IsolationResult) -> Self {
let common: HashSet<_> = first
.isolated_vertices
.intersection(&second.isolated_vertices)
.copied()
.collect();
let only_first: HashSet<_> = first
.isolated_vertices
.difference(&second.isolated_vertices)
.copied()
.collect();
let only_second: HashSet<_> = second
.isolated_vertices
.difference(&first.isolated_vertices)
.copied()
.collect();
let union_size = first.isolated_vertices.len() + second.isolated_vertices.len()
- common.len();
let jaccard = if union_size > 0 {
common.len() as f64 / union_size as f64
} else {
1.0 };
Self {
common_isolated: common,
only_first,
only_second,
jaccard_similarity: jaccard,
}
}
pub fn is_identical(&self) -> bool {
self.only_first.is_empty() && self.only_second.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_no_isolation() {
let result = IsolationResult::no_isolation();
assert!(!result.has_isolation());
assert_eq!(result.num_isolated(), 0);
assert!(result.is_verified);
}
#[test]
fn test_isolation_result() {
let mut isolated = HashSet::new();
isolated.insert(1);
isolated.insert(2);
isolated.insert(3);
let result = IsolationResult {
isolated_vertices: isolated,
cut_edges: vec![(3, 4), (3, 5)],
cut_value: 2.5,
num_high_energy_edges: 2,
threshold: 1.0,
is_verified: true,
};
assert!(result.has_isolation());
assert_eq!(result.num_isolated(), 3);
assert_eq!(result.num_cut_edges(), 2);
assert!(result.is_isolated(1));
assert!(!result.is_isolated(4));
}
#[test]
fn test_boundary_vertices() {
let mut isolated = HashSet::new();
isolated.insert(1);
isolated.insert(2);
isolated.insert(3);
let result = IsolationResult {
isolated_vertices: isolated,
cut_edges: vec![(3, 4), (2, 5)],
cut_value: 2.0,
num_high_energy_edges: 1,
threshold: 1.0,
is_verified: true,
};
let boundary = result.boundary_vertices();
assert!(boundary.contains(&3));
assert!(boundary.contains(&2));
assert!(!boundary.contains(&1)); }
#[test]
fn test_region() {
let mut vertices = HashSet::new();
vertices.insert(1);
vertices.insert(2);
vertices.insert(3);
let region = IsolationRegion {
vertices,
internal_edges: vec![(1, 2), (2, 3)],
boundary_edges: vec![(3, 4)],
total_energy: 5.0,
boundary_weight: 1.0,
region_id: 0,
};
assert_eq!(region.num_vertices(), 3);
assert_eq!(region.num_internal_edges(), 2);
assert_eq!(region.num_boundary_edges(), 1);
assert!((region.avg_energy() - 2.5).abs() < 0.01);
assert!(region.contains(1));
assert!(!region.contains(4));
}
#[test]
fn test_comparison() {
let mut isolated1 = HashSet::new();
isolated1.insert(1);
isolated1.insert(2);
isolated1.insert(3);
let result1 = IsolationResult {
isolated_vertices: isolated1,
cut_edges: vec![],
cut_value: 0.0,
num_high_energy_edges: 0,
threshold: 1.0,
is_verified: true,
};
let mut isolated2 = HashSet::new();
isolated2.insert(2);
isolated2.insert(3);
isolated2.insert(4);
let result2 = IsolationResult {
isolated_vertices: isolated2,
cut_edges: vec![],
cut_value: 0.0,
num_high_energy_edges: 0,
threshold: 1.0,
is_verified: true,
};
let comparison = IsolationComparison::compare(&result1, &result2);
assert_eq!(comparison.common_isolated.len(), 2); assert_eq!(comparison.only_first.len(), 1); assert_eq!(comparison.only_second.len(), 1); assert!(!comparison.is_identical());
assert!(comparison.jaccard_similarity > 0.0 && comparison.jaccard_similarity < 1.0);
}
}