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

Qiskit / qiskit / 25853702590

14 May 2026 09:54AM UTC coverage: 87.425% (-0.2%) from 87.632%
25853702590

Pull #16145

github

web-flow
Merge b64e22949 into dcc06fb27
Pull Request #16145: Add generated `qiskit-pyo3-ffi` crate

0 of 317 new or added lines in 4 files covered. (0.0%)

247 existing lines in 12 files now uncovered.

107175 of 122591 relevant lines covered (87.42%)

956757.34 hits per line

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

63.51
/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 https://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 std::cell::RefCell;
20
use std::error::Error;
21

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

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

51
    /// Map the given `condition` so that it only references variables in the destination
52
    /// circuit (as given to this class on initialization).
53
    ///
54
    /// If `allow_reorder` is `true`, then when a legacy condition (the two-tuple form) is made
55
    /// on a register that has a counterpart in the destination with all the same (mapped) bits but
56
    /// in a different order, then that register will be used and the value suitably modified to
57
    /// make the equality condition work.  This is maintaining legacy (tested) behavior of
58
    /// [DAGCircuit::compose]; nowhere else does this, and in general this would require *far*
59
    /// more complex classical rewriting than Qiskit needs to worry about in the full expression
60
    /// era.
61
    pub fn map_condition<F, E>(
52✔
62
        &self,
52✔
63
        condition: &Condition,
52✔
64
        allow_reorder: bool,
52✔
65
        mut add_register: F,
52✔
66
    ) -> Result<Condition, E>
52✔
67
    where
52✔
68
        F: FnMut(&ClassicalRegister) -> Result<(), E>,
52✔
69
        E: Error,
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::<_, E>)
×
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,
×
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, E>(
12✔
142
        &self,
12✔
143
        target: &SwitchTarget,
12✔
144
        mut add_register: F,
12✔
145
    ) -> Result<SwitchTarget, E>
12✔
146
    where
12✔
147
        F: FnMut(&ClassicalRegister) -> Result<(), E>,
12✔
148
        E: Error,
12✔
149
    {
150
        Ok(match target {
12✔
151
            SwitchTarget::Bit(bit) => SwitchTarget::Bit(self.bit_map.get(bit).cloned().unwrap()),
×
152
            SwitchTarget::Register(register) => {
×
UNCOV
153
                SwitchTarget::Register(self.map_register(register, &mut add_register)?)
×
154
            }
155
            SwitchTarget::Expr(expr) => SwitchTarget::Expr(self.map_expr(expr, &mut add_register)?),
12✔
156
        })
157
    }
12✔
158

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

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

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

228
        self.register_map
10✔
229
            .borrow_mut()
10✔
230
            .insert(theirs.name().to_string(), mapped_theirs.clone());
10✔
231
        Ok(mapped_theirs)
10✔
232
    }
12✔
233
}
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