| |
| |
| |
| |
| |
| |
| |
| |
|
|
| use crate::{AlgorithmError, AlgorithmResult}; |
| use std::collections::HashMap; |
|
|
| |
| #[derive(Debug, Clone)] |
| pub struct Graph { |
| |
| pub vertices: usize, |
|
|
| |
| pub edges: Vec<Vec<usize>>, |
| } |
|
|
| impl Graph { |
| |
| pub fn new(vertices: usize) -> Self { |
| Graph { |
| vertices, |
| edges: vec![Vec::new(); vertices], |
| } |
| } |
|
|
| |
| pub fn add_edge(&mut self, u: usize, v: usize) -> AlgorithmResult<()> { |
| if u >= self.vertices || v >= self.vertices { |
| return Err(AlgorithmError::InvalidGraph("Vertex out of range".to_string())); |
| } |
|
|
| if u == v { |
| return Err(AlgorithmError::InvalidGraph("No self-loops".to_string())); |
| } |
|
|
| if !self.edges[u].contains(&v) { |
| self.edges[u].push(v); |
| } |
| if !self.edges[v].contains(&u) { |
| self.edges[v].push(u); |
| } |
|
|
| Ok(()) |
| } |
|
|
| |
| pub fn neighbors(&self, v: usize) -> AlgorithmResult<Vec<usize>> { |
| if v >= self.vertices { |
| return Err(AlgorithmError::InvalidGraph("Vertex out of range".to_string())); |
| } |
| Ok(self.edges[v].clone()) |
| } |
|
|
| |
| pub fn degree(&self, v: usize) -> AlgorithmResult<usize> { |
| if v >= self.vertices { |
| return Err(AlgorithmError::InvalidGraph("Vertex out of range".to_string())); |
| } |
| Ok(self.edges[v].len()) |
| } |
|
|
| |
| pub fn is_regular(&self) -> AlgorithmResult<bool> { |
| if self.vertices == 0 { |
| return Ok(true); |
| } |
|
|
| let d = self.degree(0)?; |
| for v in 0..self.vertices { |
| if self.degree(v)? != d { |
| return Ok(false); |
| } |
| } |
| Ok(true) |
| } |
| } |
|
|
| |
| #[derive(Debug, Clone)] |
| pub struct CoinedWalkState { |
| |
| pub position_probs: Vec<f64>, |
|
|
| |
| pub coin_state: u8, |
|
|
| |
| pub steps: usize, |
| } |
|
|
| impl CoinedWalkState { |
| |
| pub fn new(vertices: usize) -> Self { |
| let mut probs = vec![0.0; vertices]; |
| probs[0] = 1.0; |
|
|
| CoinedWalkState { |
| position_probs: probs, |
| coin_state: 0, |
| steps: 0, |
| } |
| } |
|
|
| |
| pub fn prob_at(&self, v: usize) -> Option<f64> { |
| if v < self.position_probs.len() { |
| Some(self.position_probs[v]) |
| } else { |
| None |
| } |
| } |
|
|
| |
| pub fn mixing_time(&self, target_uniformity: f64) -> Option<usize> { |
| let n = self.position_probs.len() as f64; |
| let uniform = 1.0 / n; |
|
|
| |
| let all_close = self |
| .position_probs |
| .iter() |
| .all(|&p| (p - uniform).abs() < target_uniformity); |
|
|
| if all_close { |
| Some(self.steps) |
| } else { |
| None |
| } |
| } |
| } |
|
|
| |
| #[derive(Debug, Clone)] |
| pub struct LineQuantumWalk { |
| |
| pub n: usize, |
|
|
| |
| pub probs: Vec<f64>, |
|
|
| |
| pub position: usize, |
|
|
| |
| pub steps: usize, |
| } |
|
|
| impl LineQuantumWalk { |
| |
| pub fn new(n: usize) -> Self { |
| let size = 2 * n + 1; |
| let mut probs = vec![0.0; size]; |
| probs[n] = 1.0; |
|
|
| LineQuantumWalk { |
| n, |
| probs, |
| position: n, |
| steps: 0, |
| } |
| } |
|
|
| |
| pub fn step(&mut self) -> AlgorithmResult<()> { |
| |
| let mut new_probs = vec![0.0; self.probs.len()]; |
|
|
| for (pos, prob) in self.probs.iter().enumerate() { |
| if *prob > 0.0 { |
| |
| if pos > 0 { |
| new_probs[pos - 1] += prob * 0.5; |
| } |
| |
| if pos < self.probs.len() - 1 { |
| new_probs[pos + 1] += prob * 0.5; |
| } |
| } |
| } |
|
|
| self.probs = new_probs; |
| self.steps += 1; |
| Ok(()) |
| } |
|
|
| |
| pub fn run(&mut self, t: usize) -> AlgorithmResult<()> { |
| for _ in 0..t { |
| self.step()?; |
| } |
| Ok(()) |
| } |
|
|
| |
| pub fn distribution(&self) -> Vec<f64> { |
| self.probs.clone() |
| } |
|
|
| |
| pub fn is_uniform(&self, tolerance: f64) -> bool { |
| let target = 1.0 / self.probs.len() as f64; |
| self.probs |
| .iter() |
| .all(|&p| (p - target).abs() < tolerance) |
| } |
| } |
|
|
| |
| #[derive(Debug, Clone)] |
| pub struct CycleQuantumWalk { |
| |
| pub n: usize, |
|
|
| |
| pub probs: Vec<f64>, |
|
|
| |
| pub steps: usize, |
| } |
|
|
| impl CycleQuantumWalk { |
| |
| pub fn new(n: usize) -> AlgorithmResult<Self> { |
| if n == 0 { |
| return Err(AlgorithmError::InvalidGraph("Cycle size must be positive".to_string())); |
| } |
|
|
| let mut probs = vec![0.0; n]; |
| probs[0] = 1.0; |
|
|
| Ok(CycleQuantumWalk { |
| n, |
| probs, |
| steps: 0, |
| }) |
| } |
|
|
| |
| pub fn step(&mut self) -> AlgorithmResult<()> { |
| let mut new_probs = vec![0.0; self.n]; |
|
|
| for (pos, prob) in self.probs.iter().enumerate() { |
| if *prob > 0.0 { |
| |
| let next = (pos + 1) % self.n; |
| new_probs[next] += prob * 0.5; |
|
|
| |
| let prev = (pos + self.n - 1) % self.n; |
| new_probs[prev] += prob * 0.5; |
| } |
| } |
|
|
| self.probs = new_probs; |
| self.steps += 1; |
| Ok(()) |
| } |
|
|
| |
| pub fn mixing_time(&mut self, tolerance: f64) -> AlgorithmResult<usize> { |
| let target = 1.0 / self.n as f64; |
|
|
| loop { |
| self.step()?; |
|
|
| let all_uniform = self |
| .probs |
| .iter() |
| .all(|&p| (p - target).abs() < tolerance); |
|
|
| if all_uniform || self.steps > 1000 { |
| break; |
| } |
| } |
|
|
| Ok(self.steps) |
| } |
|
|
| |
| pub fn spectral_gap(&self) -> f64 { |
| |
| 2.0 - 2.0 * (2.0 * std::f64::consts::PI / self.n as f64).cos() |
| } |
| } |
|
|
| |
| #[derive(Debug, Clone)] |
| pub struct AdjacencyMatrixWalk { |
| |
| pub matrix: Vec<Vec<f64>>, |
|
|
| |
| pub probs: Vec<f64>, |
|
|
| |
| pub steps: usize, |
| } |
|
|
| impl AdjacencyMatrixWalk { |
| |
| pub fn from_graph(graph: &Graph) -> AlgorithmResult<Self> { |
| let n = graph.vertices; |
| let mut matrix = vec![vec![0.0; n]; n]; |
|
|
| for u in 0..n { |
| let deg = graph.degree(u)?; |
| for &v in &graph.edges[u] { |
| matrix[u][v] = 1.0 / deg as f64; |
| } |
| } |
|
|
| let mut probs = vec![0.0; n]; |
| probs[0] = 1.0; |
|
|
| Ok(AdjacencyMatrixWalk { |
| matrix, |
| probs, |
| steps: 0, |
| }) |
| } |
|
|
| |
| pub fn step(&mut self) { |
| let n = self.probs.len(); |
| let mut new_probs = vec![0.0; n]; |
|
|
| for i in 0..n { |
| for j in 0..n { |
| new_probs[i] += self.matrix[j][i] * self.probs[j]; |
| } |
| } |
|
|
| self.probs = new_probs; |
| self.steps += 1; |
| } |
|
|
| |
| pub fn run(&mut self, t: usize) { |
| for _ in 0..t { |
| self.step(); |
| } |
| } |
|
|
| |
| pub fn stationary_distribution(&self) -> Vec<f64> { |
| |
| vec![1.0 / self.probs.len() as f64; self.probs.len()] |
| } |
| } |
|
|
| #[cfg(test)] |
| mod tests { |
| use super::*; |
|
|
| #[test] |
| fn test_graph_creation() { |
| let graph = Graph::new(3); |
| assert_eq!(graph.vertices, 3); |
| } |
|
|
| #[test] |
| fn test_graph_add_edge() { |
| let mut graph = Graph::new(3); |
| assert!(graph.add_edge(0, 1).is_ok()); |
| assert!(graph.add_edge(0, 0).is_err()); |
| } |
|
|
| #[test] |
| fn test_graph_neighbors() { |
| let mut graph = Graph::new(3); |
| graph.add_edge(0, 1).unwrap(); |
| graph.add_edge(0, 2).unwrap(); |
|
|
| let neighbors = graph.neighbors(0).unwrap(); |
| assert_eq!(neighbors.len(), 2); |
| } |
|
|
| #[test] |
| fn test_coined_walk_state() { |
| let state = CoinedWalkState::new(5); |
| assert_eq!(state.prob_at(0), Some(1.0)); |
| assert_eq!(state.prob_at(1), Some(0.0)); |
| } |
|
|
| #[test] |
| fn test_line_quantum_walk() { |
| let walk = LineQuantumWalk::new(10); |
| assert_eq!(walk.probs.len(), 21); |
| } |
|
|
| #[test] |
| fn test_line_walk_step() { |
| let mut walk = LineQuantumWalk::new(5); |
| assert!(walk.step().is_ok()); |
| assert_eq!(walk.steps, 1); |
| } |
|
|
| #[test] |
| fn test_cycle_quantum_walk() { |
| let walk = CycleQuantumWalk::new(5); |
| assert!(walk.is_ok()); |
| } |
|
|
| #[test] |
| fn test_cycle_walk_step() { |
| let mut walk = CycleQuantumWalk::new(4).unwrap(); |
| assert!(walk.step().is_ok()); |
| assert_eq!(walk.steps, 1); |
| } |
|
|
| #[test] |
| fn test_spectral_gap() { |
| let walk = CycleQuantumWalk::new(4).unwrap(); |
| let gap = walk.spectral_gap(); |
| assert!(gap > 0.0); |
| } |
|
|
| #[test] |
| fn test_adjacency_matrix_walk() { |
| let mut graph = Graph::new(3); |
| graph.add_edge(0, 1).unwrap(); |
| graph.add_edge(1, 2).unwrap(); |
| graph.add_edge(2, 0).unwrap(); |
|
|
| let walk = AdjacencyMatrixWalk::from_graph(&graph); |
| assert!(walk.is_ok()); |
| } |
| } |
|
|
| |
|
|