quantrs2_core/quantum_walk/
graph.rs1use crate::error::{QuantRS2Error, QuantRS2Result};
4use scirs2_core::ndarray::Array2;
5use scirs2_core::Complex64;
6use std::collections::VecDeque;
7
8use super::eigensolvers::{compute_laplacian_eigenvalues_impl, estimate_fiedler_value_impl};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum GraphType {
13 Line,
15 Cycle,
17 Complete,
19 Hypercube,
21 Grid2D,
23 Custom,
25}
26
27#[derive(Debug, Clone)]
29pub enum CoinOperator {
30 Hadamard,
32 Grover,
34 DFT,
36 Custom(Array2<Complex64>),
38}
39
40#[derive(Debug, Clone)]
42pub struct SearchOracle {
43 pub marked: Vec<usize>,
45}
46
47impl SearchOracle {
48 pub const fn new(marked: Vec<usize>) -> Self {
50 Self { marked }
51 }
52
53 pub fn is_marked(&self, vertex: usize) -> bool {
55 self.marked.contains(&vertex)
56 }
57}
58
59#[derive(Debug, Clone)]
61pub struct Graph {
62 pub num_vertices: usize,
64 pub edges: Vec<Vec<usize>>,
66 pub weights: Option<Vec<Vec<f64>>>,
68}
69
70impl Graph {
71 pub fn new(graph_type: GraphType, size: usize) -> Self {
73 let mut graph = Self {
74 num_vertices: match graph_type {
75 GraphType::Hypercube => 1 << size, GraphType::Grid2D => size * size, _ => size,
78 },
79 edges: vec![],
80 weights: None,
81 };
82
83 graph.edges = vec![Vec::new(); graph.num_vertices];
85
86 match graph_type {
87 GraphType::Line => {
88 for i in 0..size.saturating_sub(1) {
89 graph.add_edge(i, i + 1);
90 }
91 }
92 GraphType::Cycle => {
93 for i in 0..size {
94 graph.add_edge(i, (i + 1) % size);
95 }
96 }
97 GraphType::Complete => {
98 for i in 0..size {
99 for j in i + 1..size {
100 graph.add_edge(i, j);
101 }
102 }
103 }
104 GraphType::Hypercube => {
105 let n = size; for i in 0..(1 << n) {
107 for j in 0..n {
108 let neighbor = i ^ (1 << j);
109 if neighbor > i {
110 graph.add_edge(i, neighbor);
111 }
112 }
113 }
114 }
115 GraphType::Grid2D => {
116 for i in 0..size {
117 for j in 0..size {
118 let idx = i * size + j;
119 if j < size - 1 {
121 graph.add_edge(idx, idx + 1);
122 }
123 if i < size - 1 {
125 graph.add_edge(idx, idx + size);
126 }
127 }
128 }
129 }
130 GraphType::Custom => {
131 }
133 }
134
135 graph
136 }
137
138 pub fn new_empty(num_vertices: usize) -> Self {
140 Self {
141 num_vertices,
142 edges: vec![Vec::new(); num_vertices],
143 weights: None,
144 }
145 }
146
147 pub fn add_edge(&mut self, u: usize, v: usize) {
149 if u < self.num_vertices && v < self.num_vertices && u != v && !self.edges[u].contains(&v) {
150 self.edges[u].push(v);
151 self.edges[v].push(u);
152 }
153 }
154
155 pub fn add_weighted_edge(&mut self, u: usize, v: usize, weight: f64) {
157 if self.weights.is_none() {
158 self.weights = Some(vec![vec![0.0; self.num_vertices]; self.num_vertices]);
159 }
160
161 self.add_edge(u, v);
162
163 if let Some(ref mut weights) = self.weights {
164 weights[u][v] = weight;
165 weights[v][u] = weight;
166 }
167 }
168
169 pub fn degree(&self, vertex: usize) -> usize {
171 if vertex < self.num_vertices {
172 self.edges[vertex].len()
173 } else {
174 0
175 }
176 }
177
178 pub fn adjacency_matrix(&self) -> Array2<f64> {
180 let mut matrix = Array2::zeros((self.num_vertices, self.num_vertices));
181
182 for (u, neighbors) in self.edges.iter().enumerate() {
183 for &v in neighbors {
184 if let Some(ref weights) = self.weights {
185 matrix[[u, v]] = weights[u][v];
186 } else {
187 matrix[[u, v]] = 1.0;
188 }
189 }
190 }
191
192 matrix
193 }
194
195 pub fn laplacian_matrix(&self) -> Array2<f64> {
197 let mut laplacian = Array2::zeros((self.num_vertices, self.num_vertices));
198
199 for v in 0..self.num_vertices {
200 let degree = self.degree(v) as f64;
201 laplacian[[v, v]] = degree;
202
203 for &neighbor in &self.edges[v] {
204 if let Some(ref weights) = self.weights {
205 laplacian[[v, neighbor]] = -weights[v][neighbor];
206 } else {
207 laplacian[[v, neighbor]] = -1.0;
208 }
209 }
210 }
211
212 laplacian
213 }
214
215 pub fn normalized_laplacian_matrix(&self) -> Array2<f64> {
217 let mut norm_laplacian = Array2::zeros((self.num_vertices, self.num_vertices));
218
219 for v in 0..self.num_vertices {
220 let degree_v = self.degree(v) as f64;
221 if degree_v == 0.0 {
222 continue;
223 }
224
225 norm_laplacian[[v, v]] = 1.0;
226
227 for &neighbor in &self.edges[v] {
228 let degree_n = self.degree(neighbor) as f64;
229 if degree_n == 0.0 {
230 continue;
231 }
232
233 let weight = if let Some(ref weights) = self.weights {
234 weights[v][neighbor]
235 } else {
236 1.0
237 };
238
239 norm_laplacian[[v, neighbor]] = -weight / (degree_v * degree_n).sqrt();
240 }
241 }
242
243 norm_laplacian
244 }
245
246 pub fn transition_matrix(&self) -> Array2<f64> {
248 let mut transition = Array2::zeros((self.num_vertices, self.num_vertices));
249
250 for v in 0..self.num_vertices {
251 let degree = self.degree(v) as f64;
252 if degree == 0.0 {
253 continue;
254 }
255
256 for &neighbor in &self.edges[v] {
257 let weight = if let Some(ref weights) = self.weights {
258 weights[v][neighbor]
259 } else {
260 1.0
261 };
262
263 transition[[v, neighbor]] = weight / degree;
264 }
265 }
266
267 transition
268 }
269
270 pub fn is_bipartite(&self) -> bool {
272 let mut colors = vec![-1; self.num_vertices];
273
274 for start in 0..self.num_vertices {
275 if colors[start] != -1 {
276 continue;
277 }
278
279 let mut queue = VecDeque::new();
280 queue.push_back(start);
281 colors[start] = 0;
282
283 while let Some(vertex) = queue.pop_front() {
284 for &neighbor in &self.edges[vertex] {
285 if colors[neighbor] == -1 {
286 colors[neighbor] = 1 - colors[vertex];
287 queue.push_back(neighbor);
288 } else if colors[neighbor] == colors[vertex] {
289 return false;
290 }
291 }
292 }
293 }
294
295 true
296 }
297
298 pub fn algebraic_connectivity(&self) -> f64 {
300 let laplacian = self.laplacian_matrix();
301
302 if self.num_vertices <= 10 {
305 self.compute_laplacian_eigenvalues(&laplacian)
306 .get(1)
307 .copied()
308 .unwrap_or(0.0)
309 } else {
310 self.estimate_fiedler_value(&laplacian)
312 }
313 }
314
315 fn compute_laplacian_eigenvalues(&self, laplacian: &Array2<f64>) -> Vec<f64> {
318 compute_laplacian_eigenvalues_impl(laplacian)
319 .unwrap_or_else(|_| vec![0.0; self.num_vertices])
320 }
321
322 fn estimate_fiedler_value(&self, laplacian: &Array2<f64>) -> f64 {
326 estimate_fiedler_value_impl(laplacian)
327 }
328
329 pub fn all_pairs_shortest_paths(&self) -> Array2<f64> {
331 let mut distances =
332 Array2::from_elem((self.num_vertices, self.num_vertices), f64::INFINITY);
333
334 for v in 0..self.num_vertices {
336 distances[[v, v]] = 0.0;
337 for &neighbor in &self.edges[v] {
338 let weight = if let Some(ref weights) = self.weights {
339 weights[v][neighbor]
340 } else {
341 1.0
342 };
343 distances[[v, neighbor]] = weight;
344 }
345 }
346
347 for k in 0..self.num_vertices {
349 for i in 0..self.num_vertices {
350 for j in 0..self.num_vertices {
351 let via_k = distances[[i, k]] + distances[[k, j]];
352 if via_k < distances[[i, j]] {
353 distances[[i, j]] = via_k;
354 }
355 }
356 }
357 }
358
359 distances
360 }
361
362 pub fn from_adjacency_matrix(matrix: &Array2<f64>) -> QuantRS2Result<Self> {
364 let (rows, cols) = matrix.dim();
365 if rows != cols {
366 return Err(QuantRS2Error::InvalidInput(
367 "Adjacency matrix must be square".to_string(),
368 ));
369 }
370
371 let mut graph = Self::new_empty(rows);
372 let mut has_weights = false;
373
374 for i in 0..rows {
375 for j in i + 1..cols {
376 let weight = matrix[[i, j]];
377 if weight != 0.0 {
378 if weight != 1.0 {
379 has_weights = true;
380 }
381 if has_weights {
382 graph.add_weighted_edge(i, j, weight);
383 } else {
384 graph.add_edge(i, j);
385 }
386 }
387 }
388 }
389
390 Ok(graph)
391 }
392}