1use crate::topology::{GateProperties, HardwareTopology, QubitProperties};
7use crate::DeviceResult;
8use petgraph::algo::{bellman_ford, floyd_warshall};
9use petgraph::graph::{NodeIndex, UnGraph};
10use petgraph::visit::EdgeRef;
11use std::collections::{HashMap, HashSet, VecDeque};
12
13pub struct TopologyAnalyzer {
15 topology: HardwareTopology,
17 distance_matrix: Option<Vec<Vec<f64>>>,
19 betweenness: Option<HashMap<u32, f64>>,
21 clustering: Option<HashMap<u32, f64>>,
23}
24
25#[derive(Debug, Clone)]
27pub struct TopologyAnalysis {
28 pub diameter: usize,
30 pub average_path_length: f64,
32 pub density: f64,
34 pub components: usize,
36 pub clustering_coefficient: f64,
38 pub central_qubits: Vec<u32>,
40 pub peripheral_qubits: Vec<u32>,
42 pub communities: Vec<HashSet<u32>>,
44 pub hardware_metrics: HardwareMetrics,
46}
47
48#[derive(Debug, Clone)]
50pub struct HardwareMetrics {
51 pub avg_gate_error: f64,
53 pub avg_t1: f64,
55 pub avg_t2: f64,
57 pub qubit_scores: HashMap<u32, f64>,
59 pub connection_scores: HashMap<(u32, u32), f64>,
61 pub premium_qubits: Vec<u32>,
63}
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
67pub enum AllocationStrategy {
68 MinimizeDistance,
70 MaximizeQuality,
72 Balanced,
74 CentralFirst,
76 MinimizeCrosstalk,
78}
79
80impl TopologyAnalyzer {
81 pub const fn new(topology: HardwareTopology) -> Self {
83 Self {
84 topology,
85 distance_matrix: None,
86 betweenness: None,
87 clustering: None,
88 }
89 }
90
91 pub fn analyze(&mut self) -> DeviceResult<TopologyAnalysis> {
93 if self.distance_matrix.is_none() {
95 self.calculate_distance_matrix();
96 }
97
98 let diameter = self.calculate_diameter();
100 let avg_path_length = self.calculate_average_path_length();
101 let density = self.calculate_density();
102 let components = self.count_components();
103
104 self.calculate_betweenness_centrality();
106 let central_qubits = self.find_central_qubits(5);
107 let peripheral_qubits = self.find_peripheral_qubits(5);
108
109 self.calculate_clustering_coefficients();
111 let clustering_coefficient = self.global_clustering_coefficient();
112
113 let communities = self.detect_communities();
115
116 let hardware_metrics = self.calculate_hardware_metrics();
118
119 Ok(TopologyAnalysis {
120 diameter,
121 average_path_length: avg_path_length,
122 density,
123 components,
124 clustering_coefficient,
125 central_qubits,
126 peripheral_qubits,
127 communities,
128 hardware_metrics,
129 })
130 }
131
132 fn calculate_distance_matrix(&mut self) {
134 let n = self.topology.num_qubits;
135 let mut matrix = vec![vec![f64::INFINITY; n]; n];
136
137 for i in 0..n {
139 matrix[i][i] = 0.0;
140 }
141
142 for edge in self.topology.connectivity.edge_references() {
144 let (a, b) = (edge.source(), edge.target());
145 let q1_id = self.topology.connectivity[a];
146 let q2_id = self.topology.connectivity[b];
147
148 let q1 = self
150 .topology
151 .qubit_properties
152 .iter()
153 .position(|q| q.id == q1_id)
154 .unwrap_or(q1_id as usize);
155 let q2 = self
156 .topology
157 .qubit_properties
158 .iter()
159 .position(|q| q.id == q2_id)
160 .unwrap_or(q2_id as usize);
161
162 let weight = *edge.weight();
163
164 matrix[q1][q2] = weight;
165 matrix[q2][q1] = weight;
166 }
167
168 for k in 0..n {
170 for i in 0..n {
171 for j in 0..n {
172 if matrix[i][k] + matrix[k][j] < matrix[i][j] {
173 matrix[i][j] = matrix[i][k] + matrix[k][j];
174 }
175 }
176 }
177 }
178
179 self.distance_matrix = Some(matrix);
180 }
181
182 fn calculate_diameter(&self) -> usize {
184 let n = self.topology.num_qubits;
187 let mut hop_matrix = vec![vec![usize::MAX; n]; n];
188
189 for i in 0..n {
191 hop_matrix[i][i] = 0;
192 }
193
194 for edge in self.topology.connectivity.edge_references() {
196 let (a, b) = (edge.source(), edge.target());
197 let q1_id = self.topology.connectivity[a];
198 let q2_id = self.topology.connectivity[b];
199
200 let q1 = self
202 .topology
203 .qubit_properties
204 .iter()
205 .position(|q| q.id == q1_id)
206 .unwrap_or(q1_id as usize);
207 let q2 = self
208 .topology
209 .qubit_properties
210 .iter()
211 .position(|q| q.id == q2_id)
212 .unwrap_or(q2_id as usize);
213
214 hop_matrix[q1][q2] = 1;
215 hop_matrix[q2][q1] = 1;
216 }
217
218 for k in 0..n {
220 for i in 0..n {
221 for j in 0..n {
222 if hop_matrix[i][k] != usize::MAX && hop_matrix[k][j] != usize::MAX {
223 let new_dist = hop_matrix[i][k] + hop_matrix[k][j];
224 if new_dist < hop_matrix[i][j] {
225 hop_matrix[i][j] = new_dist;
226 }
227 }
228 }
229 }
230 }
231
232 hop_matrix
234 .iter()
235 .flat_map(|row| row.iter())
236 .filter(|&&d| d != usize::MAX)
237 .copied()
238 .max()
239 .unwrap_or(0)
240 }
241
242 fn calculate_average_path_length(&self) -> f64 {
244 let matrix = self.distance_matrix.as_ref().expect(
245 "distance_matrix must be calculated before calling calculate_average_path_length",
246 );
247 let n = self.topology.num_qubits;
248 let mut sum = 0.0;
249 let mut count = 0;
250
251 for i in 0..n {
252 for j in i + 1..n {
253 if matrix[i][j] != f64::INFINITY {
254 sum += matrix[i][j];
255 count += 1;
256 }
257 }
258 }
259
260 if count > 0 {
261 sum / count as f64
262 } else {
263 0.0
264 }
265 }
266
267 fn calculate_density(&self) -> f64 {
269 let n = self.topology.num_qubits as f64;
270 let m = self.topology.connectivity.edge_count() as f64;
271
272 if n > 1.0 {
273 2.0 * m / (n * (n - 1.0))
274 } else {
275 0.0
276 }
277 }
278
279 fn count_components(&self) -> usize {
281 use petgraph::algo::connected_components;
282 connected_components(&self.topology.connectivity)
283 }
284
285 fn calculate_betweenness_centrality(&mut self) {
287 let n = self.topology.num_qubits;
288 let mut betweenness = HashMap::new();
289
290 for i in 0..n {
292 betweenness.insert(i as u32, 0.0);
293 }
294
295 for s in 0..n {
297 let (distances, predecessors) = self.single_source_shortest_paths(s);
299
300 let mut delta = vec![0.0; n];
302
303 let mut nodes_by_distance: Vec<_> =
305 (0..n).filter(|&v| distances[v] != f64::INFINITY).collect();
306 nodes_by_distance.sort_by(|&a, &b| {
307 distances[b]
308 .partial_cmp(&distances[a])
309 .unwrap_or(std::cmp::Ordering::Equal)
310 });
311
312 for &w in &nodes_by_distance {
313 for &v in &predecessors[w] {
314 let num_paths = if distances[v] + 1.0 == distances[w] {
315 1.0
316 } else {
317 0.0
318 };
319 delta[v] += num_paths * (1.0 + delta[w]);
320 }
321
322 if w != s {
323 if let Some(value) = betweenness.get_mut(&(w as u32)) {
324 *value += delta[w];
325 }
326 }
327 }
328 }
329
330 let norm = if n > 2 {
332 2.0 / ((n - 1) * (n - 2)) as f64
333 } else {
334 1.0
335 };
336 for value in betweenness.values_mut() {
337 *value *= norm;
338 }
339
340 self.betweenness = Some(betweenness);
341 }
342
343 fn single_source_shortest_paths(&self, source: usize) -> (Vec<f64>, Vec<Vec<usize>>) {
345 let n = self.topology.num_qubits;
346 let mut distances = vec![f64::INFINITY; n];
347 let mut predecessors = vec![Vec::new(); n];
348 let mut queue = VecDeque::new();
349
350 distances[source] = 0.0;
351 queue.push_back(source);
352
353 while let Some(u) = queue.pop_front() {
354 if let Some(node_u) = self
356 .topology
357 .connectivity
358 .node_indices()
359 .find(|&n| self.topology.connectivity[n] == u as u32)
360 {
361 for neighbor in self.topology.connectivity.neighbors(node_u) {
363 let v = self.topology.connectivity[neighbor] as usize;
364
365 if distances[v] == f64::INFINITY {
366 distances[v] = distances[u] + 1.0;
367 predecessors[v].push(u);
368 queue.push_back(v);
369 } else if distances[v] == distances[u] + 1.0 {
370 predecessors[v].push(u);
371 }
372 }
373 }
374 }
375
376 (distances, predecessors)
377 }
378
379 fn find_central_qubits(&self, count: usize) -> Vec<u32> {
381 let betweenness = match self.betweenness.as_ref() {
382 Some(b) => b,
383 None => return Vec::new(),
384 };
385
386 let mut qubits: Vec<_> = betweenness.iter().map(|(&q, &b)| (q, b)).collect();
387
388 qubits.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
389 qubits.into_iter().take(count).map(|(q, _)| q).collect()
390 }
391
392 fn find_peripheral_qubits(&self, count: usize) -> Vec<u32> {
394 let betweenness = match self.betweenness.as_ref() {
395 Some(b) => b,
396 None => return Vec::new(),
397 };
398
399 let mut qubits: Vec<_> = betweenness.iter().map(|(&q, &b)| (q, b)).collect();
400
401 qubits.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
402 qubits.into_iter().take(count).map(|(q, _)| q).collect()
403 }
404
405 fn calculate_clustering_coefficients(&mut self) {
407 let mut clustering = HashMap::new();
408
409 for node in self.topology.connectivity.node_indices() {
410 let qubit = self.topology.connectivity[node];
411 let neighbors: Vec<_> = self.topology.connectivity.neighbors(node).collect();
412
413 let k = neighbors.len();
414 if k < 2 {
415 clustering.insert(qubit, 0.0);
416 continue;
417 }
418
419 let mut edges = 0;
421 for i in 0..neighbors.len() {
422 for j in i + 1..neighbors.len() {
423 if self
424 .topology
425 .connectivity
426 .contains_edge(neighbors[i], neighbors[j])
427 {
428 edges += 1;
429 }
430 }
431 }
432
433 let coefficient = 2.0 * edges as f64 / (k * (k - 1)) as f64;
434 clustering.insert(qubit, coefficient);
435 }
436
437 self.clustering = Some(clustering);
438 }
439
440 fn global_clustering_coefficient(&self) -> f64 {
442 let coefficients = match self.clustering.as_ref() {
443 Some(c) => c,
444 None => return 0.0,
445 };
446 if coefficients.is_empty() {
447 return 0.0;
448 }
449
450 coefficients.values().sum::<f64>() / coefficients.len() as f64
451 }
452
453 fn detect_communities(&self) -> Vec<HashSet<u32>> {
455 use petgraph::algo::tarjan_scc;
458
459 let mut communities = Vec::new();
460 let sccs = tarjan_scc(&self.topology.connectivity);
461
462 for scc in sccs {
463 let community: HashSet<u32> = scc
464 .into_iter()
465 .map(|n| self.topology.connectivity[n])
466 .collect();
467 communities.push(community);
468 }
469
470 communities
471 }
472
473 fn calculate_hardware_metrics(&self) -> HardwareMetrics {
475 let mut total_gate_error = 0.0;
476 let mut gate_count = 0;
477
478 for props in self.topology.gate_properties.values() {
479 total_gate_error += props.error_rate;
480 gate_count += 1;
481 }
482
483 let avg_gate_error = if gate_count > 0 {
484 total_gate_error / gate_count as f64
485 } else {
486 0.0
487 };
488
489 let avg_t1 = self
491 .topology
492 .qubit_properties
493 .iter()
494 .map(|q| q.t1)
495 .sum::<f64>()
496 / self.topology.qubit_properties.len() as f64;
497
498 let avg_t2 = self
499 .topology
500 .qubit_properties
501 .iter()
502 .map(|q| q.t2)
503 .sum::<f64>()
504 / self.topology.qubit_properties.len() as f64;
505
506 let mut qubit_scores = HashMap::new();
508 for qubit in &self.topology.qubit_properties {
509 let score = self.calculate_qubit_score(qubit);
510 qubit_scores.insert(qubit.id, score);
511 }
512
513 let mut connection_scores = HashMap::new();
515 for ((q1, q2), props) in &self.topology.gate_properties {
516 let score = 1.0 - props.error_rate; connection_scores.insert((*q1, *q2), score);
518 }
519
520 let mut sorted_qubits: Vec<_> = qubit_scores
522 .iter()
523 .map(|(&id, &score)| (id, score))
524 .collect();
525 sorted_qubits.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
526
527 let premium_count = (sorted_qubits.len() as f64 * 0.2).ceil() as usize;
528 let premium_qubits = sorted_qubits
529 .into_iter()
530 .take(premium_count)
531 .map(|(id, _)| id)
532 .collect();
533
534 HardwareMetrics {
535 avg_gate_error,
536 avg_t1,
537 avg_t2,
538 qubit_scores,
539 connection_scores,
540 premium_qubits,
541 }
542 }
543
544 fn calculate_qubit_score(&self, qubit: &QubitProperties) -> f64 {
546 let t1_score = (qubit.t1 / 100.0).min(1.0); let t2_score = (qubit.t2 / 100.0).min(1.0);
549 let gate_score = 1.0 - qubit.single_qubit_gate_error;
550 let readout_score = 1.0 - qubit.readout_error;
551
552 0.2_f64.mul_add(
554 readout_score,
555 0.2_f64.mul_add(gate_score, 0.3_f64.mul_add(t1_score, 0.3 * t2_score)),
556 )
557 }
558
559 pub fn allocate_qubits(
561 &mut self,
562 num_logical_qubits: usize,
563 strategy: AllocationStrategy,
564 ) -> DeviceResult<Vec<u32>> {
565 if num_logical_qubits > self.topology.num_qubits {
566 return Err(crate::DeviceError::InsufficientQubits {
567 required: num_logical_qubits,
568 available: self.topology.num_qubits,
569 });
570 }
571
572 let analysis = self.analyze()?;
574
575 match strategy {
576 AllocationStrategy::MinimizeDistance => {
577 self.allocate_minimize_distance(num_logical_qubits)
578 }
579 AllocationStrategy::MaximizeQuality => {
580 self.allocate_maximize_quality(num_logical_qubits, &analysis)
581 }
582 AllocationStrategy::Balanced => self.allocate_balanced(num_logical_qubits, &analysis),
583 AllocationStrategy::CentralFirst => Ok(analysis
584 .central_qubits
585 .into_iter()
586 .take(num_logical_qubits)
587 .collect()),
588 AllocationStrategy::MinimizeCrosstalk => {
589 self.allocate_minimize_crosstalk(num_logical_qubits)
590 }
591 }
592 }
593
594 fn allocate_minimize_distance(&self, num_qubits: usize) -> DeviceResult<Vec<u32>> {
596 let matrix = self.distance_matrix.as_ref().ok_or_else(|| {
598 crate::DeviceError::DeviceNotInitialized("Distance matrix not calculated".to_string())
599 })?;
600 let n = self.topology.num_qubits;
601
602 let mut best_allocation = Vec::new();
603 let mut best_score = f64::INFINITY;
604
605 for start in 0..n {
607 let mut allocation = vec![start as u32];
608 let mut remaining: HashSet<_> = (0..n).filter(|&i| i != start).collect();
609
610 while allocation.len() < num_qubits && !remaining.is_empty() {
612 let mut best_next = None;
613 let mut best_dist = f64::INFINITY;
614
615 for &candidate in &remaining {
616 let total_dist: f64 = allocation
617 .iter()
618 .map(|&q| matrix[q as usize][candidate])
619 .sum();
620
621 if total_dist < best_dist {
622 best_dist = total_dist;
623 best_next = Some(candidate);
624 }
625 }
626
627 if let Some(next) = best_next {
628 allocation.push(next as u32);
629 remaining.remove(&next);
630 }
631 }
632
633 let mut total_dist = 0.0;
635 let mut count = 0;
636 for i in 0..allocation.len() {
637 for j in i + 1..allocation.len() {
638 total_dist += matrix[allocation[i] as usize][allocation[j] as usize];
639 count += 1;
640 }
641 }
642
643 let avg_dist = if count > 0 {
644 total_dist / count as f64
645 } else {
646 0.0
647 };
648
649 if avg_dist < best_score {
650 best_score = avg_dist;
651 best_allocation = allocation;
652 }
653 }
654
655 Ok(best_allocation)
656 }
657
658 fn allocate_maximize_quality(
660 &self,
661 num_qubits: usize,
662 analysis: &TopologyAnalysis,
663 ) -> DeviceResult<Vec<u32>> {
664 let mut qubits: Vec<_> = analysis
666 .hardware_metrics
667 .qubit_scores
668 .iter()
669 .map(|(&id, &score)| (id, score))
670 .collect();
671
672 qubits.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
673
674 Ok(qubits
675 .into_iter()
676 .take(num_qubits)
677 .map(|(id, _)| id)
678 .collect())
679 }
680
681 fn allocate_balanced(
683 &self,
684 num_qubits: usize,
685 analysis: &TopologyAnalysis,
686 ) -> DeviceResult<Vec<u32>> {
687 let mut scores = HashMap::new();
689
690 let betweenness = self.betweenness.as_ref();
691 for (&qubit, &quality) in &analysis.hardware_metrics.qubit_scores {
692 let centrality = betweenness
693 .and_then(|b| b.get(&qubit))
694 .copied()
695 .unwrap_or(0.0);
696 let score = 0.6f64.mul_add(quality, 0.4 * centrality);
698 scores.insert(qubit, score);
699 }
700
701 let mut sorted: Vec<_> = scores.into_iter().collect();
702 sorted.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
703
704 Ok(sorted
705 .into_iter()
706 .take(num_qubits)
707 .map(|(id, _)| id)
708 .collect())
709 }
710
711 fn allocate_minimize_crosstalk(&self, num_qubits: usize) -> DeviceResult<Vec<u32>> {
713 let matrix = self.distance_matrix.as_ref().ok_or_else(|| {
715 crate::DeviceError::DeviceNotInitialized("Distance matrix not calculated".to_string())
716 })?;
717 let n = self.topology.num_qubits;
718
719 let mut allocation = Vec::new();
720 let mut remaining: HashSet<_> = (0..n).collect();
721
722 let start = 0;
724 allocation.push(start as u32);
725 remaining.remove(&start);
726
727 while allocation.len() < num_qubits && !remaining.is_empty() {
729 let mut best_candidate = None;
730 let mut best_min_dist = 0.0;
731
732 for &candidate in &remaining {
733 let min_dist = allocation
734 .iter()
735 .map(|&q| matrix[q as usize][candidate])
736 .fold(f64::INFINITY, f64::min);
737
738 if min_dist > best_min_dist {
739 best_min_dist = min_dist;
740 best_candidate = Some(candidate);
741 }
742 }
743
744 if let Some(candidate) = best_candidate {
745 allocation.push(candidate as u32);
746 remaining.remove(&candidate);
747 }
748 }
749
750 Ok(allocation)
751 }
752
753 pub fn recommend_swap_paths(&self, source: u32, target: u32) -> Vec<Vec<u32>> {
755 let n = self.topology.num_qubits;
758
759 let mut paths = Vec::new();
761 let mut queue = VecDeque::new();
762 queue.push_back((vec![source], HashSet::new()));
763
764 while let Some((path, mut visited)) = queue.pop_front() {
765 let current = match path.last() {
766 Some(&c) => c,
767 None => continue,
768 };
769
770 if current == target {
771 paths.push(path);
772 if paths.len() >= 3 {
773 break;
775 }
776 continue;
777 }
778
779 visited.insert(current);
780
781 if let Some(node) = self
783 .topology
784 .connectivity
785 .node_indices()
786 .find(|&n| self.topology.connectivity[n] == current)
787 {
788 for neighbor in self.topology.connectivity.neighbors(node) {
789 let next = self.topology.connectivity[neighbor];
790 if !visited.contains(&next) {
791 let mut new_path = path.clone();
792 new_path.push(next);
793 queue.push_back((new_path, visited.clone()));
794 }
795 }
796 }
797 }
798
799 paths
800 }
801}
802
803pub fn create_standard_topology(
805 topology_type: &str,
806 size: usize,
807) -> DeviceResult<HardwareTopology> {
808 match topology_type {
809 "linear" => create_linear_topology(size),
810 "grid" => create_grid_topology(size),
811 "heavy_hex" => create_heavy_hex_topology(size),
812 "star" => create_star_topology(size),
813 _ => Err(crate::DeviceError::UnsupportedDevice(format!(
814 "Unknown topology type: {topology_type}"
815 ))),
816 }
817}
818
819fn create_linear_topology(size: usize) -> DeviceResult<HardwareTopology> {
821 let mut topology = HardwareTopology::new(size);
822
823 for i in 0..size {
825 let props = QubitProperties {
826 id: i as u32,
827 index: i as u32,
828 t1: (i as f64).mul_add(2.0, 50.0), t2: (i as f64).mul_add(1.5, 30.0), single_qubit_gate_error: 0.001,
831 gate_error_1q: 0.001,
832 readout_error: 0.01,
833 frequency: (i as f64).mul_add(0.01, 5.0),
834 };
835 topology.add_qubit(props);
836 }
837
838 for i in 0..size - 1 {
840 let props = GateProperties {
841 error_rate: 0.01,
842 duration: 200.0,
843 gate_type: "CZ".to_string(),
844 };
845 topology.add_connection(i as u32, (i + 1) as u32, props);
846 }
847
848 Ok(topology)
849}
850
851fn create_grid_topology(size: usize) -> DeviceResult<HardwareTopology> {
853 let side = (size as f64).sqrt().ceil() as usize;
854 let mut topology = HardwareTopology::new(size);
855
856 for i in 0..size {
858 let props = QubitProperties {
859 id: i as u32,
860 index: i as u32,
861 t1: (i as f64).mul_add(1.5, 40.0),
862 t2: (i as f64).mul_add(1.0, 25.0),
863 single_qubit_gate_error: 0.001,
864 gate_error_1q: 0.001,
865 readout_error: 0.01,
866 frequency: (i as f64).mul_add(0.01, 5.0),
867 };
868 topology.add_qubit(props);
869 }
870
871 for row in 0..side {
873 for col in 0..side {
874 let idx = row * side + col;
875 if idx >= size {
876 break;
877 }
878
879 if col + 1 < side && idx + 1 < size {
881 let props = GateProperties {
882 error_rate: 0.01,
883 duration: 200.0,
884 gate_type: "CZ".to_string(),
885 };
886 topology.add_connection(idx as u32, (idx + 1) as u32, props);
887 }
888
889 if row + 1 < side && idx + side < size {
891 let props = GateProperties {
892 error_rate: 0.01,
893 duration: 200.0,
894 gate_type: "CZ".to_string(),
895 };
896 topology.add_connection(idx as u32, (idx + side) as u32, props);
897 }
898 }
899 }
900
901 Ok(topology)
902}
903
904fn create_heavy_hex_topology(size: usize) -> DeviceResult<HardwareTopology> {
906 let mut topology = HardwareTopology::new(size);
908
909 for i in 0..size {
911 let props = QubitProperties {
912 id: i as u32,
913 index: i as u32,
914 t1: (i as f64).mul_add(3.0, 30.0),
915 t2: (i as f64).mul_add(2.0, 20.0),
916 single_qubit_gate_error: (i as f64).mul_add(0.0001, 0.001),
917 gate_error_1q: (i as f64).mul_add(0.0001, 0.001),
918 readout_error: (i as f64).mul_add(0.001, 0.01),
919 frequency: (i as f64).mul_add(0.01, 5.0),
920 };
921 topology.add_qubit(props);
922 }
923
924 for i in 0..size {
926 let connections = match i % 6 {
928 0 => vec![1, 5],
929 1 => vec![0, 2],
930 2 => vec![1, 3],
931 3 => vec![2, 4],
932 4 => vec![3, 5],
933 5 => vec![4, 0],
934 _ => vec![],
935 };
936
937 for &j in &connections {
938 if i < j && j < size {
939 let props = GateProperties {
940 error_rate: ((i + j) as f64).mul_add(0.0001, 0.01),
941 duration: 200.0,
942 gate_type: "CZ".to_string(),
943 };
944 topology.add_connection(i as u32, j as u32, props);
945 }
946 }
947 }
948
949 Ok(topology)
950}
951
952fn create_star_topology(size: usize) -> DeviceResult<HardwareTopology> {
954 if size < 2 {
955 return Err(crate::DeviceError::UnsupportedDevice(
956 "Star topology requires at least 2 qubits".to_string(),
957 ));
958 }
959
960 let mut topology = HardwareTopology::new(size);
961
962 for i in 0..size {
964 let props = QubitProperties {
965 id: i as u32,
966 index: i as u32,
967 t1: (i as f64).mul_add(2.0, 50.0),
968 t2: (i as f64).mul_add(1.5, 30.0),
969 single_qubit_gate_error: 0.001,
970 gate_error_1q: 0.001,
971 readout_error: 0.01,
972 frequency: (i as f64).mul_add(0.01, 5.0),
973 };
974 topology.add_qubit(props);
975 }
976
977 for i in 1..size {
979 let props = GateProperties {
980 error_rate: 0.01,
981 duration: 200.0,
982 gate_type: "CZ".to_string(),
983 };
984 topology.add_connection(0, i as u32, props);
985 }
986
987 Ok(topology)
988}
989
990#[cfg(test)]
991mod tests {
992 use super::*;
993
994 #[test]
995 fn test_linear_topology_analysis() {
996 let topology = create_linear_topology(5).expect("Failed to create linear topology");
997 let mut analyzer = TopologyAnalyzer::new(topology);
998 let analysis = analyzer.analyze().expect("Failed to analyze topology");
999
1000 assert_eq!(analysis.diameter, 4); assert_eq!(analysis.components, 1); assert!(analysis.density < 0.5); }
1004
1005 #[test]
1006 fn test_grid_topology_analysis() {
1007 let topology = create_grid_topology(9).expect("Failed to create grid topology"); let mut analyzer = TopologyAnalyzer::new(topology);
1009 let analysis = analyzer.analyze().expect("Failed to analyze topology");
1010
1011 assert_eq!(analysis.components, 1);
1012 assert!(analysis.diameter <= 4); assert_eq!(analysis.clustering_coefficient, 0.0);
1015 }
1016
1017 #[test]
1018 fn test_qubit_allocation() {
1019 let topology = create_grid_topology(9).expect("Failed to create grid topology");
1020 let mut analyzer = TopologyAnalyzer::new(topology);
1021
1022 let alloc1 = analyzer
1024 .allocate_qubits(4, AllocationStrategy::MinimizeDistance)
1025 .expect("Failed to allocate qubits with MinimizeDistance strategy");
1026 assert_eq!(alloc1.len(), 4);
1027
1028 let alloc2 = analyzer
1029 .allocate_qubits(4, AllocationStrategy::MaximizeQuality)
1030 .expect("Failed to allocate qubits with MaximizeQuality strategy");
1031 assert_eq!(alloc2.len(), 4);
1032
1033 let alloc3 = analyzer
1034 .allocate_qubits(4, AllocationStrategy::CentralFirst)
1035 .expect("Failed to allocate qubits with CentralFirst strategy");
1036 assert_eq!(alloc3.len(), 4);
1037 }
1038
1039 #[test]
1040 fn test_swap_paths() {
1041 let topology = create_linear_topology(5).expect("Failed to create linear topology");
1042 let analyzer = TopologyAnalyzer::new(topology);
1043
1044 let paths = analyzer.recommend_swap_paths(0, 4);
1045 assert!(!paths.is_empty());
1046 assert_eq!(paths[0].len(), 5); }
1048}