• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Qiskit / qiskit / 18175065500

01 Oct 2025 08:41PM UTC coverage: 88.174% (-0.07%) from 88.241%
18175065500

Pull #14568

github

web-flow
Merge ada78bb0a into cbe1f20ca
Pull Request #14568: Port control-flow operations to Rust.

2497 of 2861 new or added lines in 59 files covered. (87.28%)

110 existing lines in 19 files now uncovered.

93893 of 106486 relevant lines covered (88.17%)

1168816.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

62.5
/crates/circuit/src/variable_mapper.rs
1
// This code is part of Qiskit.
2
//
3
// (C) Copyright IBM 2025
4
//
5
// This code is licensed under the Apache License, Version 2.0. You may
6
// obtain a copy of this license in the LICENSE.txt file in the root directory
7
// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8
//
9
// Any modifications or derivative works of this code must retain this
10
// copyright notice, and modified files need to carry a notice indicating
11
// that they have been altered from the originals.
12

13
use crate::bit::{ClassicalRegister, Register, ShareableClbit};
14
use crate::classical::expr;
15
use crate::operations::{Condition, SwitchTarget};
16
use hashbrown::{HashMap, HashSet};
17
use num_bigint::BigUint;
18
use num_traits::Num;
19
use pyo3::PyResult;
20
use pyo3::prelude::*;
21
use std::cell::RefCell;
22

23
pub(crate) struct VariableMapper {
24
    target_cregs: Vec<ClassicalRegister>,
25
    register_map: RefCell<HashMap<String, ClassicalRegister>>,
26
    bit_map: HashMap<ShareableClbit, ShareableClbit>,
27
    var_map: HashMap<expr::Var, expr::Var>,
28
    stretch_map: HashMap<expr::Stretch, expr::Stretch>,
29
}
30

31
impl VariableMapper {
32
    /// Constructs a new mapper.
33
    ///
34
    /// The `stretch_map` is only used for direct calls to [VariableMapper::map_expr]
35
    /// since `condition`s and `target`s expressions are never durations. Provide
36
    /// [None] if you don't need this.
37
    pub fn new(
108,756✔
38
        target_cregs: Vec<ClassicalRegister>,
108,756✔
39
        bit_map: HashMap<ShareableClbit, ShareableClbit>,
108,756✔
40
        var_map: HashMap<expr::Var, expr::Var>,
108,756✔
41
        stretch_map: Option<HashMap<expr::Stretch, expr::Stretch>>,
108,756✔
42
    ) -> Self {
108,756✔
43
        Self {
108,756✔
44
            target_cregs,
108,756✔
45
            register_map: RefCell::default(),
108,756✔
46
            bit_map,
108,756✔
47
            var_map,
108,756✔
48
            stretch_map: stretch_map.unwrap_or_default(),
108,756✔
49
        }
108,756✔
50
    }
108,756✔
51

52
    /// Map the given `condition` so that it only references variables in the destination
53
    /// circuit (as given to this class on initialization).
54
    ///
55
    /// If `allow_reorder` is `true`, then when a legacy condition (the two-tuple form) is made
56
    /// on a register that has a counterpart in the destination with all the same (mapped) bits but
57
    /// in a different order, then that register will be used and the value suitably modified to
58
    /// make the equality condition work.  This is maintaining legacy (tested) behavior of
59
    /// [DAGCircuit::compose]; nowhere else does this, and in general this would require *far*
60
    /// more complex classical rewriting than Qiskit needs to worry about in the full expression
61
    /// era.
62
    pub fn map_condition<F>(
52✔
63
        &self,
52✔
64
        condition: &Condition,
52✔
65
        allow_reorder: bool,
52✔
66
        mut add_register: F,
52✔
67
    ) -> PyResult<Condition>
52✔
68
    where
52✔
69
        F: FnMut(&ClassicalRegister) -> PyResult<()>,
52✔
70
    {
71
        Ok(match condition {
52✔
72
            Condition::Bit(target, value) => {
38✔
73
                Condition::Bit(self.bit_map.get(target).unwrap().clone(), *value)
38✔
74
            }
75
            Condition::Expr(e) => Condition::Expr(self.map_expr(e, &mut add_register)?),
12✔
76
            Condition::Register(target, value) => {
2✔
77
                if !allow_reorder {
2✔
78
                    return Ok(Condition::Register(
79
                        self.map_register(target, &mut add_register)?,
2✔
80
                        value.clone(),
2✔
81
                    ));
82
                }
×
83
                // This is maintaining the legacy behavior of `DAGCircuit.compose`.  We don't
84
                // attempt to speed-up this lookup with a cache, since that would just make the more
85
                // standard cases more annoying to deal with.
86

87
                let mapped_bits_order = target
×
88
                    .bits()
×
89
                    .map(|b| self.bit_map.get(&b).unwrap().clone())
×
90
                    .collect::<Vec<_>>();
×
91
                let mapped_bits_set: HashSet<ShareableClbit> =
×
92
                    mapped_bits_order.iter().cloned().collect();
×
93

94
                let mapped_theirs = self
×
95
                    .target_cregs
×
96
                    .iter()
×
97
                    .find(|register| {
×
98
                        let register_set: HashSet<ShareableClbit> = register.iter().collect();
×
99
                        mapped_bits_set == register_set
×
100
                    })
×
101
                    .cloned()
×
102
                    .map(Ok::<_, PyErr>)
×
103
                    .unwrap_or_else(|| {
×
104
                        let mapped_theirs =
×
105
                            ClassicalRegister::new_alias(None, mapped_bits_order.clone());
×
106
                        add_register(&mapped_theirs)?;
×
107
                        Ok(mapped_theirs)
×
108
                    })?;
×
109

110
                let new_order: HashMap<ShareableClbit, usize> = mapped_bits_order
×
111
                    .into_iter()
×
112
                    .enumerate()
×
113
                    .map(|(i, bit)| (bit, i))
×
114
                    .collect();
×
115

116
                // Build the little-endian bit string
117
                let value_bits: Vec<char> = format!("{:0width$b}", value, width = target.len())
×
118
                    .chars()
×
119
                    .rev()
×
120
                    .collect();
×
121

122
                // Reorder bits and reverse again to go back to big-endian for final conversion
123
                let mapped_str: String = mapped_theirs
×
124
                    .iter() // TODO: we should probably not need to collect to Vec here. Why do we?
×
125
                    .collect::<Vec<_>>()
×
126
                    .into_iter()
×
127
                    .map(|bit| value_bits[*new_order.get(&bit).unwrap()])
×
128
                    .rev()
×
129
                    .collect();
×
130

131
                Condition::Register(
×
132
                    mapped_theirs,
×
NEW
133
                    BigUint::from_str_radix(&mapped_str, 2).unwrap(),
×
134
                )
×
135
            }
136
        })
137
    }
52✔
138

139
    /// Map the real-time variables in a `target` of a `SwitchCaseOp` to the new
140
    /// circuit.
141
    pub fn map_target<F>(
12✔
142
        &self,
12✔
143
        target: &SwitchTarget,
12✔
144
        mut add_register: F,
12✔
145
    ) -> PyResult<SwitchTarget>
12✔
146
    where
12✔
147
        F: FnMut(&ClassicalRegister) -> PyResult<()>,
12✔
148
    {
149
        Ok(match target {
12✔
150
            SwitchTarget::Bit(bit) => SwitchTarget::Bit(self.bit_map.get(bit).cloned().unwrap()),
×
151
            SwitchTarget::Register(register) => {
×
152
                SwitchTarget::Register(self.map_register(register, &mut add_register)?)
×
153
            }
154
            SwitchTarget::Expr(expr) => SwitchTarget::Expr(self.map_expr(expr, &mut add_register)?),
12✔
155
        })
156
    }
12✔
157

158
    /// Map the variables in an [expr::Expr] node to the new circuit.
159
    pub fn map_expr<F>(&self, expr: &expr::Expr, mut add_register: F) -> PyResult<expr::Expr>
24✔
160
    where
24✔
161
        F: FnMut(&ClassicalRegister) -> PyResult<()>,
24✔
162
    {
163
        let mut mapped = expr.clone();
24✔
164
        mapped.visit_mut(|e| match e {
58✔
165
            expr::ExprRefMut::Var(var) => match var {
28✔
166
                expr::Var::Standalone { .. } => {
167
                    if let Some(mapping) = self.var_map.get(var).cloned() {
6✔
168
                        *var = mapping;
6✔
169
                    }
6✔
170
                    Ok(())
6✔
171
                }
172
                expr::Var::Bit { bit } => {
12✔
173
                    let bit = self.bit_map.get(bit).cloned().unwrap();
12✔
174
                    *var = expr::Var::Bit { bit };
12✔
175
                    Ok(())
12✔
176
                }
177
                expr::Var::Register { register, ty } => {
10✔
178
                    let ty = *ty;
10✔
179
                    let register = self.map_register(register, &mut add_register)?;
10✔
180
                    *var = expr::Var::Register { register, ty };
10✔
181
                    Ok(())
10✔
182
                }
183
            },
184
            expr::ExprRefMut::Stretch(stretch) => {
×
185
                if let Some(mapping) = self.stretch_map.get(stretch).cloned() {
×
186
                    *stretch = mapping;
×
187
                }
×
188
                Ok(())
×
189
            }
190
            _ => Ok(()),
30✔
191
        })?;
58✔
192
        Ok(mapped)
24✔
193
    }
24✔
194

195
    /// Map the target's registers to suitable equivalents in the destination, adding an
196
    /// extra one if there's no exact match."""
197
    fn map_register<F>(
12✔
198
        &self,
12✔
199
        theirs: &ClassicalRegister,
12✔
200
        mut add_register: F,
12✔
201
    ) -> PyResult<ClassicalRegister>
12✔
202
    where
12✔
203
        F: FnMut(&ClassicalRegister) -> PyResult<()>,
12✔
204
    {
205
        if let Some(mapped_theirs) = self.register_map.borrow().get(theirs.name()) {
12✔
206
            return Ok(mapped_theirs.clone());
2✔
207
        }
10✔
208

209
        let mapped_bits: Vec<_> = theirs.iter().map(|b| self.bit_map[&b].clone()).collect();
22✔
210
        let mapped_theirs = self
10✔
211
            .target_cregs
10✔
212
            .iter()
10✔
213
            .find(|register| {
14✔
214
                let register: Vec<_> = register.bits().collect();
14✔
215
                mapped_bits == register
14✔
216
            })
14✔
217
            .cloned()
10✔
218
            .map(Ok::<_, PyErr>)
10✔
219
            .unwrap_or_else(|| {
10✔
220
                let mapped_theirs = ClassicalRegister::new_alias(None, mapped_bits.clone());
×
221
                add_register(&mapped_theirs)?;
×
222
                Ok(mapped_theirs)
×
223
            })?;
×
224

225
        self.register_map
10✔
226
            .borrow_mut()
10✔
227
            .insert(theirs.name().to_string(), mapped_theirs.clone());
10✔
228
        Ok(mapped_theirs)
10✔
229
    }
12✔
230
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc