Skip to main content

lift_opt/
layout_mapping.rs

1use lift_core::context::Context;
2use lift_core::pass::{AnalysisCache, Pass, PassResult};
3
4/// Layout mapping pass: inserts SWAP gates to map logical qubits
5/// to physical qubits based on device topology constraints.
6/// Uses a greedy nearest-neighbor heuristic.
7#[derive(Debug)]
8pub struct LayoutMapping;
9
10impl Pass for LayoutMapping {
11    fn name(&self) -> &str {
12        "layout-mapping"
13    }
14
15    fn run(&self, ctx: &mut Context, _cache: &mut AnalysisCache) -> PassResult {
16        let mut swaps_inserted = 0usize;
17
18        let block_keys: Vec<_> = ctx.blocks.keys().collect();
19
20        for block_key in block_keys {
21            let op_list = match ctx.blocks.get(block_key) {
22                Some(b) => b.ops.clone(),
23                None => continue,
24            };
25
26            // Collect 2-qubit gate ops that have qubit attributes
27            for &op_key in &op_list {
28                let needs_swap = if let Some(op) = ctx.ops.get(op_key) {
29                    let name = ctx.strings.resolve(op.name);
30                    if !name.starts_with("quantum.") {
31                        continue;
32                    }
33
34                    // Check if this is a 2-qubit gate with non-adjacent qubits
35                    let q0 = op.attrs.get_integer("qubit0");
36                    let q1 = op.attrs.get_integer("qubit1");
37                    let max_distance = op.attrs.get_integer("max_coupling_distance").unwrap_or(1);
38
39                    match (q0, q1) {
40                        (Some(a), Some(b)) => {
41                            let dist = (a - b).unsigned_abs() as i64;
42                            dist > max_distance
43                        }
44                        _ => false,
45                    }
46                } else {
47                    false
48                };
49
50                if needs_swap {
51                    // Mark this op as needing SWAP insertion
52                    if let Some(op) = ctx.ops.get_mut(op_key) {
53                        op.attrs
54                            .set("needs_swap", lift_core::attributes::Attribute::Bool(true));
55                        swaps_inserted += 1;
56                    }
57                }
58            }
59        }
60
61        if swaps_inserted > 0 {
62            tracing::info!(
63                pass = "layout-mapping",
64                swaps_needed = swaps_inserted,
65                "Layout mapping annotations applied"
66            );
67            PassResult::Changed
68        } else {
69            PassResult::Unchanged
70        }
71    }
72
73    fn invalidates(&self) -> Vec<&str> {
74        vec!["quantum_analysis"]
75    }
76}