1use std::collections::{HashMap, HashSet, VecDeque};
9use std::hash::Hash;
10use thiserror::Error;
11
12use crate::ising::{IsingError, IsingResult};
13
14#[derive(Error, Debug)]
16pub enum EmbeddingError {
17 #[error("Ising error: {0}")]
19 IsingError(#[from] IsingError),
20
21 #[error("Embedding not found: {0}")]
23 EmbeddingNotFound(String),
24
25 #[error("Invalid embedding: {0}")]
27 InvalidEmbedding(String),
28
29 #[error("Topology error: {0}")]
31 TopologyError(String),
32}
33
34#[derive(Debug, Clone)]
36pub struct EmbeddingResult {
37 pub embedding: HashMap<usize, Vec<usize>>,
39 pub chain_strength: f64,
41 pub success: bool,
43 pub error_message: Option<String>,
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49pub enum HardwareTopology {
50 Chimera(usize, usize, usize),
53 Pegasus(usize),
55 Zephyr(usize),
57 Custom,
59}
60
61#[derive(Debug, Clone)]
63pub struct HardwareGraph {
64 pub num_qubits: usize,
66 pub adjacency: HashMap<usize, Vec<usize>>,
68 pub topology: HardwareTopology,
70 pub coordinates: Option<HashMap<usize, Vec<usize>>>,
72}
73
74impl HardwareGraph {
75 #[must_use]
77 pub fn new_custom(num_qubits: usize, edges: Vec<(usize, usize)>) -> Self {
78 let mut adjacency = HashMap::new();
79
80 for i in 0..num_qubits {
81 adjacency.insert(i, Vec::new());
82 }
83
84 for (u, v) in edges {
85 adjacency
86 .get_mut(&u)
87 .expect("key was just inserted")
88 .push(v);
89 adjacency
90 .get_mut(&v)
91 .expect("key was just inserted")
92 .push(u);
93 }
94
95 Self {
96 num_qubits,
97 adjacency,
98 topology: HardwareTopology::Custom,
99 coordinates: None,
100 }
101 }
102
103 pub fn new_chimera(m: usize, n: usize, t: usize) -> IsingResult<Self> {
105 let num_qubits = 2 * m * n * t;
106 let mut adjacency = HashMap::new();
107
108 for q in 0..num_qubits {
110 adjacency.insert(q, Vec::new());
111 }
112
113 for i in 0..m {
115 for j in 0..n {
116 let cell_offset = 2 * t * (i * n + j);
117
118 for k in 0..t {
120 for l in 0..t {
121 let q1 = cell_offset + k;
122 let q2 = cell_offset + t + l;
123 adjacency
124 .get_mut(&q1)
125 .expect("key was just inserted")
126 .push(q2);
127 adjacency
128 .get_mut(&q2)
129 .expect("key was just inserted")
130 .push(q1);
131 }
132 }
133
134 if j < n - 1 {
136 for k in 0..t {
137 let q1 = cell_offset + k;
138 let q2 = cell_offset + 2 * t + k;
139 adjacency
140 .get_mut(&q1)
141 .expect("key was just inserted")
142 .push(q2);
143 adjacency
144 .get_mut(&q2)
145 .expect("key was just inserted")
146 .push(q1);
147 }
148 }
149
150 if i < m - 1 {
152 for k in 0..t {
153 let q1 = cell_offset + t + k;
154 let q2 = cell_offset + 2 * t * n + t + k;
155 adjacency
156 .get_mut(&q1)
157 .expect("key was just inserted")
158 .push(q2);
159 adjacency
160 .get_mut(&q2)
161 .expect("key was just inserted")
162 .push(q1);
163 }
164 }
165 }
166 }
167
168 let mut coordinates = HashMap::new();
170 for i in 0..m {
171 for j in 0..n {
172 let cell_offset = 2 * t * (i * n + j);
173 for k in 0..t {
174 coordinates.insert(cell_offset + k, vec![i, j, 0, k]);
175 coordinates.insert(cell_offset + t + k, vec![i, j, 1, k]);
176 }
177 }
178 }
179
180 Ok(Self {
181 num_qubits,
182 adjacency,
183 topology: HardwareTopology::Chimera(m, n, t),
184 coordinates: Some(coordinates),
185 })
186 }
187
188 #[must_use]
190 pub fn neighbors(&self, qubit: usize) -> Vec<usize> {
191 if qubit >= self.num_qubits {
192 return Vec::new();
193 }
194
195 self.adjacency.get(&qubit).cloned().unwrap_or_default()
196 }
197
198 #[must_use]
200 pub fn are_connected(&self, q1: usize, q2: usize) -> bool {
201 if q1 >= self.num_qubits || q2 >= self.num_qubits {
202 return false;
203 }
204 self.adjacency
205 .get(&q1)
206 .is_some_and(|neighbors| neighbors.contains(&q2))
207 }
208}
209
210#[derive(Debug, Clone)]
212pub struct Embedding {
213 pub chains: HashMap<usize, Vec<usize>>,
215 pub qubit_to_variable: HashMap<usize, usize>,
217}
218
219impl Embedding {
220 #[must_use]
222 pub fn new() -> Self {
223 Self {
224 chains: HashMap::new(),
225 qubit_to_variable: HashMap::new(),
226 }
227 }
228
229 pub fn add_chain(&mut self, variable: usize, chain: Vec<usize>) -> IsingResult<()> {
231 for &qubit in &chain {
233 if let Some(&existing_var) = self.qubit_to_variable.get(&qubit) {
234 if existing_var != variable {
235 return Err(IsingError::InvalidQubit(qubit));
236 }
237 }
238 }
239
240 for &qubit in &chain {
242 self.qubit_to_variable.insert(qubit, variable);
243 }
244 self.chains.insert(variable, chain);
245
246 Ok(())
247 }
248
249 pub fn verify(
251 &self,
252 logical_edges: &[(usize, usize)],
253 num_vars: usize,
254 hardware: &HardwareGraph,
255 ) -> IsingResult<()> {
256 for var in 0..num_vars {
258 if !self.chains.contains_key(&var) {
259 return Err(IsingError::InvalidQubit(var));
260 }
261 }
262
263 for (var, chain) in &self.chains {
265 if !is_chain_connected(chain, hardware) {
266 return Err(IsingError::HardwareConstraint(format!(
267 "Chain for variable {var} is not connected"
268 )));
269 }
270 }
271
272 for &(u, v) in logical_edges {
274 let Some(chain1) = self.chains.get(&u) else {
275 return Err(IsingError::InvalidQubit(u));
276 };
277 let Some(chain2) = self.chains.get(&v) else {
278 return Err(IsingError::InvalidQubit(v));
279 };
280
281 let mut connected = false;
282 'outer: for &q1 in chain1 {
283 for &q2 in chain2 {
284 if hardware.are_connected(q1, q2) {
285 connected = true;
286 break 'outer;
287 }
288 }
289 }
290
291 if !connected {
292 return Err(IsingError::HardwareConstraint(format!(
293 "Logical edge ({u}, {v}) has no physical connection"
294 )));
295 }
296 }
297
298 Ok(())
299 }
300}
301
302pub struct MinorMiner {
304 pub max_tries: usize,
306 pub chain_length_penalty: f64,
308 pub random_init: bool,
310 pub seed: Option<u64>,
312}
313
314impl Default for MinorMiner {
315 fn default() -> Self {
316 Self {
317 max_tries: 10,
318 chain_length_penalty: 1.0,
319 random_init: true,
320 seed: None,
321 }
322 }
323}
324
325impl MinorMiner {
326 pub fn find_embedding(
328 &self,
329 logical_edges: &[(usize, usize)],
330 num_vars: usize,
331 hardware: &HardwareGraph,
332 ) -> IsingResult<Embedding> {
333 for attempt in 0..self.max_tries {
335 let mut embedding = Embedding::new();
336 let mut used_qubits = HashSet::new();
337
338 let mut var_degrees: Vec<(usize, usize)> = (0..num_vars)
340 .map(|v| {
341 let degree = logical_edges
342 .iter()
343 .filter(|&&(u, w)| u == v || w == v)
344 .count();
345 (v, degree)
346 })
347 .collect();
348 var_degrees.sort_by_key(|&(_, d)| std::cmp::Reverse(d));
349
350 let mut success = true;
352 for (var, _) in var_degrees {
353 let embedded_neighbors = get_embedded_neighbors(var, logical_edges, &embedding);
355
356 if let Some(chain) = find_chain_for_variable(
358 var,
359 &embedded_neighbors,
360 &embedding,
361 hardware,
362 &used_qubits,
363 ) {
364 for &q in &chain {
365 used_qubits.insert(q);
366 }
367 embedding.add_chain(var, chain)?;
368 } else {
369 success = false;
370 break;
371 }
372 }
373
374 if success {
375 embedding.verify(logical_edges, num_vars, hardware)?;
377 return Ok(embedding);
378 }
379 }
380
381 Err(IsingError::HardwareConstraint(
382 "Failed to find valid embedding".to_string(),
383 ))
384 }
385}
386
387fn is_chain_connected(chain: &[usize], hardware: &HardwareGraph) -> bool {
389 if chain.is_empty() {
390 return false;
391 }
392 if chain.len() == 1 {
393 return true;
394 }
395
396 let mut visited = HashSet::new();
398 let mut queue = VecDeque::new();
399 queue.push_back(chain[0]);
400 visited.insert(chain[0]);
401
402 while let Some(qubit) = queue.pop_front() {
403 for neighbor in hardware.neighbors(qubit) {
404 if chain.contains(&neighbor) && visited.insert(neighbor) {
405 queue.push_back(neighbor);
406 }
407 }
408 }
409
410 visited.len() == chain.len()
411}
412
413fn get_embedded_neighbors(
415 var: usize,
416 logical_edges: &[(usize, usize)],
417 embedding: &Embedding,
418) -> Vec<usize> {
419 let mut neighbors = Vec::new();
420
421 for &(u, v) in logical_edges {
422 if u == var && embedding.chains.contains_key(&v) {
423 neighbors.push(v);
424 } else if v == var && embedding.chains.contains_key(&u) {
425 neighbors.push(u);
426 }
427 }
428
429 neighbors
430}
431
432fn find_chain_for_variable(
434 var: usize,
435 embedded_neighbors: &[usize],
436 embedding: &Embedding,
437 hardware: &HardwareGraph,
438 used_qubits: &HashSet<usize>,
439) -> Option<Vec<usize>> {
440 if embedded_neighbors.is_empty() {
442 for q in 0..hardware.num_qubits {
443 if !used_qubits.contains(&q) {
444 return Some(vec![q]);
445 }
446 }
447 return None;
448 }
449
450 let mut candidate_qubits = HashSet::new();
452
453 for &neighbor_var in embedded_neighbors {
455 if let Some(neighbor_chain) = embedding.chains.get(&neighbor_var) {
456 for &q in neighbor_chain {
457 for &adj_q in &hardware.neighbors(q) {
458 if !used_qubits.contains(&adj_q) {
459 candidate_qubits.insert(adj_q);
460 }
461 }
462 }
463 }
464 }
465
466 if candidate_qubits.is_empty() {
468 for q in 0..hardware.num_qubits {
469 if !used_qubits.contains(&q) {
470 candidate_qubits.insert(q);
471 }
472 }
473 }
474
475 let candidates: Vec<_> = candidate_qubits.into_iter().collect();
477 for &start_q in &candidates {
478 let chain = grow_chain(
479 start_q,
480 embedded_neighbors,
481 embedding,
482 hardware,
483 used_qubits,
484 );
485
486 if is_valid_chain(&chain, embedded_neighbors, embedding, hardware) {
487 return Some(chain);
488 }
489 }
490
491 if !embedded_neighbors.is_empty() {
493 for &start_q in &candidates {
495 let mut chain = vec![start_q];
496 let mut chain_set = HashSet::new();
497 chain_set.insert(start_q);
498
499 for &neighbor_var in embedded_neighbors {
501 if let Some(neighbor_chain) = embedding.chains.get(&neighbor_var) {
502 for &nq in neighbor_chain {
503 if hardware.are_connected(start_q, nq) {
504 return Some(chain);
505 }
506 }
507 }
508 }
509 }
510 }
511
512 None
513}
514
515fn grow_chain(
517 start: usize,
518 embedded_neighbors: &[usize],
519 embedding: &Embedding,
520 hardware: &HardwareGraph,
521 used_qubits: &HashSet<usize>,
522) -> Vec<usize> {
523 let mut chain = vec![start];
524 let mut chain_set = HashSet::new();
525 chain_set.insert(start);
526
527 for &neighbor_var in embedded_neighbors {
529 if let Some(neighbor_chain) = embedding.chains.get(&neighbor_var) {
530 let mut connected = false;
532 for &q in &chain {
533 for &nq in neighbor_chain {
534 if hardware.are_connected(q, nq) {
535 connected = true;
536 break;
537 }
538 }
539 if connected {
540 break;
541 }
542 }
543
544 if !connected {
545 if let Some(path) =
547 find_path_to_chain(&chain, neighbor_chain, hardware, used_qubits, &chain_set)
548 {
549 for q in path {
550 if chain_set.insert(q) {
551 chain.push(q);
552 }
553 }
554 }
555 }
556 }
557 }
558
559 chain
560}
561
562fn find_path_to_chain(
564 from_chain: &[usize],
565 to_chain: &[usize],
566 hardware: &HardwareGraph,
567 used_qubits: &HashSet<usize>,
568 chain_set: &HashSet<usize>,
569) -> Option<Vec<usize>> {
570 let mut queue = VecDeque::new();
572 let mut parent = HashMap::new();
573 let mut visited = HashSet::new();
574
575 for &q in from_chain {
577 queue.push_back(q);
578 visited.insert(q);
579 }
580
581 while let Some(current) = queue.pop_front() {
582 for neighbor in hardware.neighbors(current) {
583 if to_chain.contains(&neighbor) {
584 let mut path = Vec::new();
586 let mut node = Some(neighbor);
587
588 while let Some(n) = node {
589 if !chain_set.contains(&n) {
590 path.push(n);
591 }
592 node = parent.get(&n).copied();
593 if from_chain.contains(&n) {
594 break;
595 }
596 }
597
598 path.reverse();
599 return Some(path);
600 }
601
602 if !used_qubits.contains(&neighbor)
603 && !chain_set.contains(&neighbor)
604 && visited.insert(neighbor)
605 {
606 parent.insert(neighbor, current);
607 queue.push_back(neighbor);
608 }
609 }
610 }
611
612 None
613}
614
615fn is_valid_chain(
617 chain: &[usize],
618 embedded_neighbors: &[usize],
619 embedding: &Embedding,
620 hardware: &HardwareGraph,
621) -> bool {
622 if chain.is_empty() {
623 return false;
624 }
625
626 if embedded_neighbors.is_empty() {
629 return true;
630 }
631
632 let mut connections = 0;
633 for &neighbor_var in embedded_neighbors {
634 if let Some(neighbor_chain) = embedding.chains.get(&neighbor_var) {
635 let mut connected = false;
636
637 'outer: for &q1 in chain {
638 for &q2 in neighbor_chain {
639 if hardware.are_connected(q1, q2) {
640 connected = true;
641 break 'outer;
642 }
643 }
644 }
645
646 if connected {
647 connections += 1;
648 }
649 }
650 }
651
652 connections > 0
654}
655
656#[cfg(test)]
657mod tests {
658 use super::*;
659
660 #[test]
661 fn test_chimera_graph() {
662 let graph = HardwareGraph::new_chimera(2, 2, 4).expect("should create Chimera graph");
663 assert_eq!(graph.num_qubits, 32);
664
665 assert!(graph.are_connected(0, 4));
667 assert!(graph.are_connected(0, 5));
668 assert!(!graph.are_connected(0, 1));
669
670 let q0_neighbors = graph.neighbors(0);
672 assert!(q0_neighbors.len() > 0);
673 }
674
675 #[test]
676 fn test_simple_embedding() {
677 let logical_edges = vec![(0, 1)];
679 let num_vars = 2;
680
681 let hardware = HardwareGraph::new_chimera(1, 1, 2).expect("should create Chimera graph");
683
684 let embedder = MinorMiner::default();
686 let result = embedder.find_embedding(&logical_edges, num_vars, &hardware);
687
688 assert!(
690 result.is_ok(),
691 "Failed to find embedding: {:?}",
692 result.err()
693 );
694
695 if let Ok(embedding) = result {
696 let verify_result = embedding.verify(&logical_edges, num_vars, &hardware);
698 assert!(
699 verify_result.is_ok(),
700 "Verification failed: {:?}",
701 verify_result.err()
702 );
703 assert_eq!(embedding.chains.len(), 2);
704
705 let chain0 = &embedding.chains[&0];
707 let chain1 = &embedding.chains[&1];
708 let mut found_connection = false;
709 for &q0 in chain0 {
710 for &q1 in chain1 {
711 if hardware.are_connected(q0, q1) {
712 found_connection = true;
713 break;
714 }
715 }
716 }
717 assert!(found_connection, "Chains are not connected");
718 }
719 }
720
721 #[test]
722 fn test_triangle_embedding() {
723 let logical_edges = vec![(0, 1), (0, 2), (1, 2)];
725 let num_vars = 3;
726
727 let hardware = HardwareGraph::new_chimera(2, 2, 2).expect("should create Chimera graph");
729
730 let embedder = MinorMiner {
732 max_tries: 20, ..Default::default()
734 };
735 let result = embedder.find_embedding(&logical_edges, num_vars, &hardware);
736
737 if let Ok(embedding) = result {
739 assert!(embedding
741 .verify(&logical_edges, num_vars, &hardware)
742 .is_ok());
743 assert_eq!(embedding.chains.len(), 3);
744 }
745 }
746
747 #[test]
748 fn test_chain_connectivity() {
749 let hardware = HardwareGraph::new_chimera(2, 2, 2).expect("should create Chimera graph");
750
751 let chain1 = vec![0, 2]; assert!(is_chain_connected(&chain1, &hardware));
754
755 let chain2 = vec![0, 7]; assert!(!is_chain_connected(&chain2, &hardware));
758
759 let chain3 = vec![0];
761 assert!(is_chain_connected(&chain3, &hardware));
762 }
763}